From 1b751781f0681a047f3d3d6097009478886ee2f5 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 27 Apr 2021 18:52:57 +0800 Subject: refactor: ... --- .../Timeline/Controllers/UserAvatarController.cs | 2 +- .../BasicServicesServiceCollectionExtensions.cs | 18 ++++++++ BackEnd/Timeline/Services/Imaging/IImageService.cs | 32 +++++++++++++ .../Timeline/Services/Imaging/ImageException.cs | 7 +-- BackEnd/Timeline/Services/Imaging/ImageService.cs | 54 ++++++++++++++++++++++ .../ImageServicesServiceCollectionExtensions.cs | 14 ++++++ .../Timeline/Services/Imaging/ImageValidator.cs | 53 --------------------- .../Services/Timeline/TimelinePostService.cs | 8 ++-- .../Timeline/Services/User/UserAvatarService.cs | 6 +-- BackEnd/Timeline/Startup.cs | 14 ++---- 10 files changed, 131 insertions(+), 77 deletions(-) create mode 100644 BackEnd/Timeline/Services/BasicServicesServiceCollectionExtensions.cs create mode 100644 BackEnd/Timeline/Services/Imaging/IImageService.cs create mode 100644 BackEnd/Timeline/Services/Imaging/ImageService.cs create mode 100644 BackEnd/Timeline/Services/Imaging/ImageServicesServiceCollectionExtensions.cs delete mode 100644 BackEnd/Timeline/Services/Imaging/ImageValidator.cs (limited to 'BackEnd') diff --git a/BackEnd/Timeline/Controllers/UserAvatarController.cs b/BackEnd/Timeline/Controllers/UserAvatarController.cs index 158c342e..d0998fa7 100644 --- a/BackEnd/Timeline/Controllers/UserAvatarController.cs +++ b/BackEnd/Timeline/Controllers/UserAvatarController.cs @@ -117,7 +117,7 @@ namespace Timeline.Controllers { ImageException.ErrorReason.CantDecode => ErrorResponse.UserAvatar.BadFormat_CantDecode(), ImageException.ErrorReason.UnmatchedFormat => ErrorResponse.UserAvatar.BadFormat_UnmatchedFormat(), - ImageException.ErrorReason.NotSquare => ErrorResponse.UserAvatar.BadFormat_BadSize(), + ImageException.ErrorReason.BadSize => ErrorResponse.UserAvatar.BadFormat_BadSize(), _ => throw new Exception(ExceptionUnknownAvatarFormatError) }); diff --git a/BackEnd/Timeline/Services/BasicServicesServiceCollectionExtensions.cs b/BackEnd/Timeline/Services/BasicServicesServiceCollectionExtensions.cs new file mode 100644 index 00000000..6465fb9f --- /dev/null +++ b/BackEnd/Timeline/Services/BasicServicesServiceCollectionExtensions.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Timeline.Services +{ + public static class BasicServicesServiceCollectionExtensions + { + public static IServiceCollection AddBasicServices(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddTransient(); + } + } +} diff --git a/BackEnd/Timeline/Services/Imaging/IImageService.cs b/BackEnd/Timeline/Services/Imaging/IImageService.cs new file mode 100644 index 00000000..9e595ff8 --- /dev/null +++ b/BackEnd/Timeline/Services/Imaging/IImageService.cs @@ -0,0 +1,32 @@ +using SixLabors.ImageSharp.Formats; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Timeline.Services.Imaging +{ + public interface IImageService + { + /// + /// Detect the format of a image. + /// + /// + /// + /// The image format. + /// Thrown when is null. + /// Thrown when image data can't be detected. + Task DetectFormatAsync(byte[] data, CancellationToken cancellationToken = default); + + /// + /// Validate a image data. + /// + /// The data of the image. Can't be null. + /// If not null, the real image format will be check against the requested format and throw if not match. If null, then do not check. + /// If true, image must be square. + /// Cancellation token. + /// The format. + /// Thrown when is null. + /// Thrown when image data can't be decoded or real type does not match request type or image is not square when required. + Task ValidateAsync(byte[] data, string? requestType = null, bool square = false, CancellationToken cancellationToken = default); + } +} diff --git a/BackEnd/Timeline/Services/Imaging/ImageException.cs b/BackEnd/Timeline/Services/Imaging/ImageException.cs index 926ecc0a..12aefa0a 100644 --- a/BackEnd/Timeline/Services/Imaging/ImageException.cs +++ b/BackEnd/Timeline/Services/Imaging/ImageException.cs @@ -19,7 +19,7 @@ namespace Timeline.Services.Imaging /// /// Image is not of required size. /// - NotSquare, + BadSize, /// /// Other unknown errer. /// @@ -42,16 +42,13 @@ namespace Timeline.Services.Imaging { ErrorReason.CantDecode => Resource.ExceptionImageReasonCantDecode, ErrorReason.UnmatchedFormat => Resource.ExceptionImageReasonUnmatchedFormat, - ErrorReason.NotSquare => Resource.ExceptionImageReasonBadSize, + ErrorReason.BadSize => Resource.ExceptionImageReasonBadSize, _ => Resource.ExceptionImageReasonUnknownError }); public ErrorReason Error { get; } -#pragma warning disable CA1819 // Properties should not return arrays public byte[]? ImageData { get; } -#pragma warning restore CA1819 // Properties should not return arrays public string? RequestType { get; } - // This field will be null if decoding failed. public string? RealType { get; } } diff --git a/BackEnd/Timeline/Services/Imaging/ImageService.cs b/BackEnd/Timeline/Services/Imaging/ImageService.cs new file mode 100644 index 00000000..9fefe3e9 --- /dev/null +++ b/BackEnd/Timeline/Services/Imaging/ImageService.cs @@ -0,0 +1,54 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Timeline.Services.Imaging +{ + + public class ImageService : IImageService + { + public async Task DetectFormatAsync(byte[] data, CancellationToken cancellationToken = default) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); + + var format = await Task.Run(() => + { + var format = Image.DetectFormat(data); + if (format is null) + { + throw new ImageException(ImageException.ErrorReason.CantDecode, data, null, null, null); + } + return format; + }, cancellationToken); + return format; + } + + public async Task ValidateAsync(byte[] data, string? requestType = null, bool square = false, CancellationToken cancellationToken = default) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); + + var format = await Task.Run(() => + { + try + { + using var image = Image.Load(data, out IImageFormat format); + if (requestType != null && !format.MimeTypes.Contains(requestType)) + throw new ImageException(ImageException.ErrorReason.UnmatchedFormat, data, requestType, format.DefaultMimeType); + if (square && image.Width != image.Height) + throw new ImageException(ImageException.ErrorReason.BadSize, data, requestType, format.DefaultMimeType); + return format; + } + catch (UnknownImageFormatException e) + { + throw new ImageException(ImageException.ErrorReason.CantDecode, data, requestType, null, null, e); + } + }, cancellationToken); + return format; + } + } +} diff --git a/BackEnd/Timeline/Services/Imaging/ImageServicesServiceCollectionExtensions.cs b/BackEnd/Timeline/Services/Imaging/ImageServicesServiceCollectionExtensions.cs new file mode 100644 index 00000000..7906b35e --- /dev/null +++ b/BackEnd/Timeline/Services/Imaging/ImageServicesServiceCollectionExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Timeline.Services.Imaging +{ + public static class ImageServicesServiceCollectionExtensions + { + public static IServiceCollection AddImageServices(this IServiceCollection services) + { + services.TryAddTransient(); + return services; + } + } +} diff --git a/BackEnd/Timeline/Services/Imaging/ImageValidator.cs b/BackEnd/Timeline/Services/Imaging/ImageValidator.cs deleted file mode 100644 index b4ae68dc..00000000 --- a/BackEnd/Timeline/Services/Imaging/ImageValidator.cs +++ /dev/null @@ -1,53 +0,0 @@ -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace Timeline.Services.Imaging -{ - public interface IImageValidator - { - /// - /// Validate a image data. - /// - /// The data of the image. Can't be null. - /// If not null, the real image format will be check against the requested format and throw if not match. If null, then do not check. - /// If true, image must be square. - /// The format. - /// Thrown when is null. - /// Thrown when image data can't be decoded or real type does not match request type or image is not square when required. - Task Validate(byte[] data, string? requestType = null, bool square = false); - } - - public class ImageValidator : IImageValidator - { - public ImageValidator() - { - } - - public async Task Validate(byte[] data, string? requestType = null, bool square = false) - { - if (data == null) - throw new ArgumentNullException(nameof(data)); - - var format = await Task.Run(() => - { - try - { - using var image = Image.Load(data, out IImageFormat format); - if (requestType != null && !format.MimeTypes.Contains(requestType)) - throw new ImageException(ImageException.ErrorReason.UnmatchedFormat, data, requestType, format.DefaultMimeType); - if (square && image.Width != image.Height) - throw new ImageException(ImageException.ErrorReason.NotSquare, data, requestType, format.DefaultMimeType); - return format; - } - catch (UnknownImageFormatException e) - { - throw new ImageException(ImageException.ErrorReason.CantDecode, data, requestType, null, null, e); - } - }); - return format; - } - } -} diff --git a/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs b/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs index 6ca2e5a0..6a6273c5 100644 --- a/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs +++ b/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs @@ -106,7 +106,7 @@ namespace Timeline.Services.Timeline /// Thrown when is of invalid format. /// Thrown when timeline does not exist. /// Thrown if user of does not exist. - /// Thrown if data is not a image. Validated by . + /// Thrown if data is not a image. Validated by . Task CreatePost(long timelineId, long authorId, TimelinePostCreateRequest request); /// @@ -167,11 +167,11 @@ namespace Timeline.Services.Timeline private readonly IBasicTimelineService _basicTimelineService; private readonly IBasicUserService _basicUserService; private readonly IDataManager _dataManager; - private readonly IImageValidator _imageValidator; + private readonly IImageService _imageValidator; private readonly IClock _clock; private readonly ColorValidator _colorValidator = new ColorValidator(); - public TimelinePostService(ILogger logger, DatabaseContext database, IBasicTimelineService basicTimelineService, IBasicUserService basicUserService, IDataManager dataManager, IImageValidator imageValidator, IClock clock) + public TimelinePostService(ILogger logger, DatabaseContext database, IBasicTimelineService basicTimelineService, IBasicUserService basicUserService, IDataManager dataManager, IImageService imageValidator, IClock clock) { _logger = logger; _database = database; @@ -309,7 +309,7 @@ namespace Timeline.Services.Timeline case MimeTypes.ImageWebp: try { - await _imageValidator.Validate(data.Data, data.ContentType); + await _imageValidator.ValidateAsync(data.Data, data.ContentType); } catch (ImageException e) { diff --git a/BackEnd/Timeline/Services/User/UserAvatarService.cs b/BackEnd/Timeline/Services/User/UserAvatarService.cs index 1b8896a6..e18a0560 100644 --- a/BackEnd/Timeline/Services/User/UserAvatarService.cs +++ b/BackEnd/Timeline/Services/User/UserAvatarService.cs @@ -120,7 +120,7 @@ namespace Timeline.Services.User private readonly DatabaseContext _database; private readonly IBasicUserService _basicUserService; private readonly IDefaultUserAvatarProvider _defaultUserAvatarProvider; - private readonly IImageValidator _imageValidator; + private readonly IImageService _imageValidator; private readonly IDataManager _dataManager; private readonly IClock _clock; @@ -129,7 +129,7 @@ namespace Timeline.Services.User DatabaseContext database, IBasicUserService basicUserService, IDefaultUserAvatarProvider defaultUserAvatarProvider, - IImageValidator imageValidator, + IImageService imageValidator, IDataManager dataManager, IClock clock) { @@ -192,7 +192,7 @@ namespace Timeline.Services.User if (avatar is null) throw new ArgumentNullException(nameof(avatar)); - await _imageValidator.Validate(avatar.Data, avatar.ContentType, true); + await _imageValidator.ValidateAsync(avatar.Data, avatar.ContentType, true); await _basicUserService.ThrowIfUserNotExist(userId); diff --git a/BackEnd/Timeline/Startup.cs b/BackEnd/Timeline/Startup.cs index 0d7bc6b3..58438d4c 100644 --- a/BackEnd/Timeline/Startup.cs +++ b/BackEnd/Timeline/Startup.cs @@ -88,22 +88,14 @@ namespace Timeline services.AddAuthorization(); services.AddSingleton(); - // TODO: Remove this. - services.TryAddSingleton(); - - services.AddSingleton(); - + services.AddBasicServices(); services.AddDatabaseManagementService(); + services.AddDataServices(); + services.AddImageServices(); services.AddAutoMapper(GetType().Assembly); services.AddMappers(); - services.AddTransient(); - - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddDbContext((services, options) => { var pathProvider = services.GetRequiredService(); -- cgit v1.2.3