From e946c2e546efae29112628c0e6d42dc145605f09 Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Mon, 19 Aug 2019 00:21:38 +0800 Subject: Improve avatar validation. --- Timeline/Controllers/UserAvatarController.cs | 36 ++++++++++++++++++++---- Timeline/Services/UserAvatarService.cs | 41 ++++++++++++++++++++-------- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/Timeline/Controllers/UserAvatarController.cs b/Timeline/Controllers/UserAvatarController.cs index 6dc767df..710ca764 100644 --- a/Timeline/Controllers/UserAvatarController.cs +++ b/Timeline/Controllers/UserAvatarController.cs @@ -19,9 +19,28 @@ namespace Timeline.Controllers public const int Put_UserNotExist = -2001; public const int Put_Forbid = -2002; + public const int Put_BadFormat_CantDecode = -2011; + public const int Put_BadFormat_UnmatchedFormat = -2012; + public const int Put_BadFormat_BadSize = -2013; public const int Delete_UserNotExist = -3001; public const int Delete_Forbid = -3002; + + + public static int From(AvatarDataException.ErrorReason error) + { + switch (error) + { + case AvatarDataException.ErrorReason.CantDecode: + return Put_BadFormat_CantDecode; + case AvatarDataException.ErrorReason.UnmatchedFormat: + return Put_BadFormat_UnmatchedFormat; + case AvatarDataException.ErrorReason.BadSize: + return Put_BadFormat_BadSize; + default: + throw new Exception("Unknown AvatarDataException.ErrorReason value."); + } + } } private readonly ILogger _logger; @@ -43,9 +62,9 @@ namespace Timeline.Controllers var avatar = await _service.GetAvatar(username); return File(avatar.Data, avatar.Type); } - catch (UserNotExistException) + catch (UserNotExistException e) { - _logger.LogInformation($"Attempt to get a avatar of a non-existent user failed. Username: {username} ."); + _logger.LogInformation(e, $"Attempt to get a avatar of a non-existent user failed. Username: {username} ."); return NotFound(new CommonResponse(ErrorCodes.Get_UserNotExist, "User does not exist.")); } } @@ -76,11 +95,16 @@ namespace Timeline.Controllers _logger.LogInformation($"Succeed to put a avatar of a user. Username: {username} ; Mime Type: {Request.ContentType} ."); return Ok(); } - catch (UserNotExistException) + catch (UserNotExistException e) { - _logger.LogInformation($"Attempt to put a avatar of a non-existent user failed. Username: {username} ."); + _logger.LogInformation(e, $"Attempt to put a avatar of a non-existent user failed. Username: {username} ."); return BadRequest(new CommonResponse(ErrorCodes.Put_UserNotExist, "User does not exist.")); } + catch (AvatarDataException e) + { + _logger.LogInformation(e, $"Attempt to put a avatar of a bad format failed. Username: {username} ."); + return BadRequest(new CommonResponse(ErrorCodes.From(e.Error), "Bad format.")); + } } [HttpDelete("users/{username}/avatar")] @@ -101,9 +125,9 @@ namespace Timeline.Controllers _logger.LogInformation($"Succeed to delete a avatar of a user. Username: {username} ."); return Ok(); } - catch (UserNotExistException) + catch (UserNotExistException e) { - _logger.LogInformation($"Attempt to delete a avatar of a non-existent user failed. Username: {username} ."); + _logger.LogInformation(e, $"Attempt to delete a avatar of a non-existent user failed. Username: {username} ."); return BadRequest(new CommonResponse(ErrorCodes.Delete_UserNotExist, "User does not exist.")); } } diff --git a/Timeline/Services/UserAvatarService.cs b/Timeline/Services/UserAvatarService.cs index 2a73cde5..dd0e5e7c 100644 --- a/Timeline/Services/UserAvatarService.cs +++ b/Timeline/Services/UserAvatarService.cs @@ -24,12 +24,29 @@ namespace Timeline.Services [Serializable] public class AvatarDataException : Exception { - public AvatarDataException(Avatar avatar, string message) : base(message) { Avatar = avatar; } - public AvatarDataException(Avatar avatar, string message, Exception inner) : base(message, inner) { Avatar = avatar; } + public enum ErrorReason + { + /// + /// Decoding image failed. + /// + CantDecode, + /// + /// Decoding succeeded but the real type is not the specified type. + /// + UnmatchedFormat, + /// + /// Image is not a square. + /// + BadSize + } + + public AvatarDataException(Avatar avatar, ErrorReason error, string message) : base(message) { Avatar = avatar; Error = error; } + public AvatarDataException(Avatar avatar, ErrorReason error, string message, Exception inner) : base(message, inner) { Avatar = avatar; Error = error; } protected AvatarDataException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + public ErrorReason Error { get; set; } public Avatar Avatar { get; set; } } @@ -49,7 +66,12 @@ namespace Timeline.Services public interface IUserAvatarValidator { - Task<(bool valid, string message)> Validate(Avatar avatar); + /// + /// Validate a avatar's format and size info. + /// + /// The avatar to validate. + /// Thrown when validation failed. + Task Validate(Avatar avatar); } public interface IUserAvatarService @@ -96,7 +118,7 @@ namespace Timeline.Services public class UserAvatarValidator : IUserAvatarValidator { - public Task<(bool valid, string message)> Validate(Avatar avatar) + public Task Validate(Avatar avatar) { return Task.Run(() => { @@ -105,15 +127,14 @@ namespace Timeline.Services using (var image = Image.Load(avatar.Data, out IImageFormat format)) { if (!format.MimeTypes.Contains(avatar.Type)) - return (false, "Image's actual mime type is not the specified one."); + throw new AvatarDataException(avatar, AvatarDataException.ErrorReason.UnmatchedFormat, "Image's actual mime type is not the specified one."); if (image.Width != image.Height) - return (false, "Image is not a square, aka width is not equal to height."); + throw new AvatarDataException(avatar, AvatarDataException.ErrorReason.BadSize, "Image is not a square, aka, width is not equal to height."); } - return (true, "A good avatar."); } catch (UnknownImageFormatException e) { - return (false, $"Failed to decode image. Exception: {e} ."); + throw new AvatarDataException(avatar, AvatarDataException.ErrorReason.CantDecode, "Failed to decode image. See inner exception.", e); } }); } @@ -196,9 +217,7 @@ namespace Timeline.Services } else { - (bool valid, string message) = await _avatarValidator.Validate(avatar); - if (!valid) - throw new AvatarDataException(avatar, $"Failed to validate image. {message}"); + await _avatarValidator.Validate(avatar); if (avatarEntity == null) { -- cgit v1.2.3