public class CreatePostHandler(
IHttpContextAccessor contextAccessor,
DatabaseContext db,
ISender mediator,
IValidator<CreatePostCommand> validator)
: RequestHandler(contextAccessor), IRequestHandler<CreatePostCommand, Result<Post>>
{
private const string StorageContainer = "images";
public async Task<Result<Post>> Handle(CreatePostCommand request, CancellationToken cancellationToken)
{
try
{
var validationResult = await ValidateInputAsync(request, cancellationToken);
if (!validationResult.IsValid)
return Result<Post>.Failure("Invalid input received.",
validationResult.ErrorMessages(), 400);
var newBlobName = $"{Guid.NewGuid()}{request.File.GetExtensionType()}";
var uploadResult = await UploadFileAsync(newBlobName, request.File, cancellationToken);
if (!uploadResult.IsSuccessful)
return uploadResult.ToResult(_ => new Post());
var uri = uploadResult.Data;
var nsfwResult = await ClassifyFileAsync(uri, cancellationToken);
if (!nsfwResult.IsSuccessful)
{
await DeleteNewlyUploadedFileAsync(newBlobName, cancellationToken);
return nsfwResult.ToResult<Post>(default!);
}
var post = new Post
{
Caption = request.Caption,
Url = uri.ToString(),
AuthorId = UserId
};
return await CreatePostAsync(post, cancellationToken);
}
catch (Exception e)
{
return Result<Post>.Failure("Something unexpected occurred.", [e.ToString()], 500);
}
}
private async Task<ValidationResult> ValidateInputAsync(CreatePostCommand request,
CancellationToken cancellationToken)
{
return await validator.ValidateAsync(request, cancellationToken);
}
private async Task DeleteNewlyUploadedFileAsync(string blobName, CancellationToken cancellationToken)
{
await mediator.Send(
new DeleteFileFromStorageCommand(blobName, StorageContainer),
cancellationToken);
}
private async Task<Result<Uri>> UploadFileAsync(string blobName, IFormFile file,
CancellationToken cancellationToken)
{
return await mediator.Send(new UploadFileToStorageCommand
{
ContainerName = StorageContainer,
BlobName = blobName,
ContentType = file.ContentType,
Stream = file.OpenReadStream()
}, cancellationToken);
}
private async Task<Result> ClassifyFileAsync(Uri uri, CancellationToken cancellationToken)
{
return await mediator.Send(new ClassifyImageCommand(uri), cancellationToken);
}
private async Task<Result<Post>> CreatePostAsync(Post post, CancellationToken cancellationToken)
{
var hashtags = await mediator.Send(new ExtractHashtagsCommand(post.Caption), cancellationToken);
foreach (var hashtag in hashtags)
post.Hashtags.Add(hashtag);
db.Posts.Add(post);
await db.SaveChangesAsync(cancellationToken);
return Result<Post>.Success(post);
}
}
public class CreatePostHandler(
IHttpContextAccessor contextAccessor,
DatabaseContext db,
ISender mediator,
IValidator<CreatePostCommand> validator)
: RequestHandler(contextAccessor), IRequestHandler<CreatePostCommand, Result<Post>>
{
private const string StorageContainer = "images";
public async Task<Result<Post>> Handle(CreatePostCommand request, CancellationToken cancellationToken)
{
try
{
var validationResult = await ValidateInputAsync(request, cancellationToken);
if (!validationResult.IsValid)
return Result<Post>.Failure("Invalid input received.",
validationResult.ErrorMessages(), 400);
var newBlobName = $"{Guid.NewGuid()}{request.File.GetExtensionType()}";
var uploadResult = await UploadFileAsync(newBlobName, request.File, cancellationToken);
if (!uploadResult.IsSuccessful)
return uploadResult.ToResult(_ => new Post());
var uri = uploadResult.Data;
var nsfwResult = await ClassifyFileAsync(uri, cancellationToken);
if (!nsfwResult.IsSuccessful)
{
await DeleteNewlyUploadedFileAsync(newBlobName, cancellationToken);
return nsfwResult.ToResult<Post>(default!);
}
var post = new Post
{
Caption = request.Caption,
Url = uri.ToString(),
AuthorId = UserId
};
return await CreatePostAsync(post, cancellationToken);
}
catch (Exception e)
{
return Result<Post>.Failure("Something unexpected occurred.", [e.ToString()], 500);
}
}
private async Task<ValidationResult> ValidateInputAsync(CreatePostCommand request,
CancellationToken cancellationToken)
{
return await validator.ValidateAsync(request, cancellationToken);
}
private async Task DeleteNewlyUploadedFileAsync(string blobName, CancellationToken cancellationToken)
{
await mediator.Send(
new DeleteFileFromStorageCommand(blobName, StorageContainer),
cancellationToken);
}
private async Task<Result<Uri>> UploadFileAsync(string blobName, IFormFile file,
CancellationToken cancellationToken)
{
return await mediator.Send(new UploadFileToStorageCommand
{
ContainerName = StorageContainer,
BlobName = blobName,
ContentType = file.ContentType,
Stream = file.OpenReadStream()
}, cancellationToken);
}
private async Task<Result> ClassifyFileAsync(Uri uri, CancellationToken cancellationToken)
{
return await mediator.Send(new ClassifyImageCommand(uri), cancellationToken);
}
private async Task<Result<Post>> CreatePostAsync(Post post, CancellationToken cancellationToken)
{
var hashtags = await mediator.Send(new ExtractHashtagsCommand(post.Caption), cancellationToken);
foreach (var hashtag in hashtags)
post.Hashtags.Add(hashtag);
db.Posts.Add(post);
await db.SaveChangesAsync(cancellationToken);
return Result<Post>.Success(post);
}
}