From fa2a3282c51d831b25f374803301e75eac15d11c Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Thu, 17 Oct 2019 20:46:57 +0800 Subject: ... --- Timeline/Controllers/TokenController.cs | 131 ++++++++++++++++---------------- Timeline/Controllers/UserController.cs | 8 +- Timeline/ErrorCodes.cs | 29 +++++++ Timeline/Helpers/Log.cs | 19 +++++ Timeline/Models/Http/Common.cs | 74 +++++++++++------- Timeline/Models/Http/Token.cs | 2 +- 6 files changed, 166 insertions(+), 97 deletions(-) create mode 100644 Timeline/ErrorCodes.cs (limited to 'Timeline') diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs index 3c166448..2e661695 100644 --- a/Timeline/Controllers/TokenController.cs +++ b/Timeline/Controllers/TokenController.cs @@ -3,39 +3,42 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using System; -using System.Collections.Generic; using System.Threading.Tasks; using Timeline.Models.Http; using Timeline.Services; -using static Timeline.Helpers.MyLogHelper; +using Timeline.Helpers; -namespace Timeline.Controllers +namespace Timeline { - [Route("token")] - [ApiController] - public class TokenController : Controller + public static partial class ErrorCodes { - private static class LoggingEventIds - { - public const int CreateSucceeded = 1000; - public const int CreateFailed = 1001; - - public const int VerifySucceeded = 2000; - public const int VerifyFailed = 2001; - } - - public static class ErrorCodes + public static partial class Http { - public const int Create_UserNotExist = -1001; - public const int Create_BadPassword = -1002; - public const int Create_BadExpireOffset = -1003; + public static class Token // bbb = 001 + { + public static class Create // cc = 01 + { + public const int BadCredential = 10010101; + } - public const int Verify_BadToken = -2001; - public const int Verify_UserNotExist = -2002; - public const int Verify_BadVersion = -2003; - public const int Verify_Expired = -2004; + public static class Verify // cc = 02 + { + public const int BadFormat = 10010201; + public const int UserNotExist = 10010202; + public const int OldVersion = 10010203; + public const int Expired = 10010204; + } + } } + } +} +namespace Timeline.Controllers +{ + [Route("token")] + [ApiController] + public class TokenController : Controller + { private readonly IUserService _userService; private readonly ILogger _logger; private readonly IClock _clock; @@ -51,23 +54,28 @@ namespace Timeline.Controllers [AllowAnonymous] public async Task Create([FromBody] CreateTokenRequest request) { - void LogFailure(string reason, int code, Exception e = null) + void LogFailure(string reason, Exception e = null) { - _logger.LogInformation(LoggingEventIds.CreateFailed, e, FormatLogMessage("Attemp to login failed.", - Pair("Reason", reason), - Pair("Code", code), - Pair("Username", request.Username), - Pair("Password", request.Password), - Pair("Expire Offset (in days)", request.ExpireOffset))); + _logger.LogInformation(e, Log.Format("Attemp to login failed.", + ("Reason", reason), + ("Username", request.Username), + ("Password", request.Password), + ("Expire (in days)", request.Expire) + )); } try { - var expiredTime = request.ExpireOffset == null ? null : (DateTime?)(_clock.GetCurrentTime().AddDays(request.ExpireOffset.Value)); - var result = await _userService.CreateToken(request.Username, request.Password, expiredTime); - _logger.LogInformation(LoggingEventIds.CreateSucceeded, FormatLogMessage("Attemp to login succeeded.", - Pair("Username", request.Username), - Pair("Expire Time", expiredTime == null ? "default" : expiredTime.Value.ToString()))); + DateTime? expireTime = null; + if (request.Expire != null) + expireTime = _clock.GetCurrentTime().AddDays(request.Expire.Value); + + var result = await _userService.CreateToken(request.Username, request.Password, expireTime); + + _logger.LogInformation(Log.Format("Attemp to login succeeded.", + ("Username", request.Username), + ("Expire At", expireTime?.ToString() ?? "default") + )); return Ok(new CreateTokenResponse { Token = result.Token, @@ -76,15 +84,15 @@ namespace Timeline.Controllers } catch (UserNotExistException e) { - var code = ErrorCodes.Create_UserNotExist; - LogFailure("User does not exist.", code, e); - return BadRequest(new CommonResponse(code, "Bad username or password.")); + LogFailure("User does not exist.", e); + return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Create.BadCredential, + "Bad username or password.")); } catch (BadPasswordException e) { - var code = ErrorCodes.Create_BadPassword; - LogFailure("Password is wrong.", code, e); - return BadRequest(new CommonResponse(code, "Bad username or password.")); + LogFailure("Password is wrong.", e); + return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Create.BadCredential, + "Bad username or password.")); } } @@ -92,22 +100,20 @@ namespace Timeline.Controllers [AllowAnonymous] public async Task Verify([FromBody] VerifyTokenRequest request) { - void LogFailure(string reason, int code, Exception e = null, params KeyValuePair[] otherProperties) + void LogFailure(string reason, Exception e = null, params (string, object)[] otherProperties) { - var properties = new KeyValuePair[3 + otherProperties.Length]; - properties[0] = Pair("Reason", reason); - properties[1] = Pair("Code", code); - properties[2] = Pair("Token", request.Token); - otherProperties.CopyTo(properties, 3); - _logger.LogInformation(LoggingEventIds.VerifyFailed, e, FormatLogMessage("Token verification failed.", properties)); + var properties = new (string, object)[2 + otherProperties.Length]; + properties[0] = ("Reason", reason); + properties[1] = ("Token", request.Token); + otherProperties.CopyTo(properties, 2); + _logger.LogInformation(e, Log.Format("Token verification failed.", properties)); } try { var result = await _userService.VerifyToken(request.Token); - _logger.LogInformation(LoggingEventIds.VerifySucceeded, - FormatLogMessage("Token verification succeeded.", - Pair("Username", result.Username), Pair("Token", request.Token))); + _logger.LogInformation(Log.Format("Token verification succeeded.", + ("Username", result.Username), ("Token", request.Token))); return Ok(new VerifyTokenResponse { User = result @@ -118,33 +124,28 @@ namespace Timeline.Controllers if (e.ErrorCode == JwtTokenVerifyException.ErrorCodes.Expired) { const string message = "Token is expired."; - var code = ErrorCodes.Verify_Expired; var innerException = e.InnerException as SecurityTokenExpiredException; - LogFailure(message, code, e, Pair("Expires", innerException.Expires)); - return BadRequest(new CommonResponse(code, message)); + LogFailure(message, e, ("Expires", innerException.Expires), ("Current Time", _clock.GetCurrentTime())); + return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Verify.Expired, message)); } else { const string message = "Token is of bad format."; - var code = ErrorCodes.Verify_BadToken; - LogFailure(message, code, e); - return BadRequest(new CommonResponse(code, message)); + LogFailure(message, e); + return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Verify.BadFormat, message)); } } catch (UserNotExistException e) { const string message = "User does not exist. Administrator might have deleted this user."; - var code = ErrorCodes.Verify_UserNotExist; - LogFailure(message, code, e); - return BadRequest(new CommonResponse(code, message)); + LogFailure(message, e); + return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Verify.UserNotExist, message)); } catch (BadTokenVersionException e) { - const string message = "Token has a old version."; - var code = ErrorCodes.Verify_BadVersion; - LogFailure(message, code, e); - _logger.LogInformation(LoggingEventIds.VerifyFailed, e, "Attemp to verify a bad token because version is old. Code: {} Token: {}.", code, request.Token); - return BadRequest(new CommonResponse(code, message)); + const string message = "Token has an old version."; + LogFailure(message, e, ("Token Version", e.TokenVersion), ("Required Version", e.RequiredVersion)); + return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Verify.OldVersion, message)); } } } diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs index bd13f0a3..c0cd3cdb 100644 --- a/Timeline/Controllers/UserController.cs +++ b/Timeline/Controllers/UserController.cs @@ -65,10 +65,10 @@ namespace Timeline.Controllers { case PutResult.Created: _logger.LogInformation(FormatLogMessage("A user is created.", Pair("Username", username))); - return CreatedAtAction("Get", new { username }, CommonPutResponse.Created); + return CreatedAtAction("Get", new { username }, CommonPutResponse.Create()); case PutResult.Modified: _logger.LogInformation(FormatLogMessage("A user is modified.", Pair("Username", username))); - return Ok(CommonPutResponse.Modified); + return Ok(CommonPutResponse.Modify()); default: throw new Exception("Unreachable code."); } @@ -102,12 +102,12 @@ namespace Timeline.Controllers { await _userService.DeleteUser(username); _logger.LogInformation(FormatLogMessage("A user is deleted.", Pair("Username", username))); - return Ok(CommonDeleteResponse.Deleted); + return Ok(CommonDeleteResponse.Delete()); } catch (UserNotExistException e) { _logger.LogInformation(e, FormatLogMessage("Attempt to delete a non-existent user.", Pair("Username", username))); - return Ok(CommonDeleteResponse.NotExists); + return Ok(CommonDeleteResponse.NotExist()); } } diff --git a/Timeline/ErrorCodes.cs b/Timeline/ErrorCodes.cs new file mode 100644 index 00000000..0b325e27 --- /dev/null +++ b/Timeline/ErrorCodes.cs @@ -0,0 +1,29 @@ +namespace Timeline +{ + /// + /// All error code constants. + /// + /// + /// Scheme: + /// abbbccdd + /// + public static partial class ErrorCodes + { + public static partial class Http // a = 1 + { + public static class Common // bbb = 000 + { + public const int InvalidModel = 10000000; + + public static class Header // cc = 01 + { + public const int Missing_ContentType = 10010101; // dd = 01 + public const int Missing_ContentLength = 10010102; // dd = 02 + public const int Zero_ContentLength = 10010103; // dd = 03 + public const int BadFormat_IfNonMatch = 10010104; // dd = 04 + } + } + } + + } +} diff --git a/Timeline/Helpers/Log.cs b/Timeline/Helpers/Log.cs index 123e8a8e..64391cd1 100644 --- a/Timeline/Helpers/Log.cs +++ b/Timeline/Helpers/Log.cs @@ -3,6 +3,7 @@ using System.Text; namespace Timeline.Helpers { + // TODO! Remember to remove this after refactor. public static class MyLogHelper { public static KeyValuePair Pair(string key, object value) => new KeyValuePair(key, value); @@ -21,4 +22,22 @@ namespace Timeline.Helpers return builder.ToString(); } } + + public static class Log + { + public static string Format(string summary, params (string, object)[] properties) + { + var builder = new StringBuilder(); + builder.Append(summary); + foreach (var property in properties) + { + var (key, value) = property; + builder.AppendLine(); + builder.Append(key); + builder.Append(" : "); + builder.Append(value); + } + return builder.ToString(); + } + } } diff --git a/Timeline/Models/Http/Common.cs b/Timeline/Models/Http/Common.cs index a72f187c..af185e85 100644 --- a/Timeline/Models/Http/Common.cs +++ b/Timeline/Models/Http/Common.cs @@ -2,43 +2,29 @@ namespace Timeline.Models.Http { public class CommonResponse { - public static class ErrorCodes - { - /// - /// Used when the model is invaid. - /// For example a required field is null. - /// - public const int InvalidModel = -100; - - public const int Header_Missing_ContentType = -111; - public const int Header_Missing_ContentLength = -112; - public const int Header_Zero_ContentLength = -113; - public const int Header_BadFormat_IfNonMatch = -114; - } - public static CommonResponse InvalidModel(string message) { - return new CommonResponse(ErrorCodes.InvalidModel, message); + return new CommonResponse(ErrorCodes.Http.Common.InvalidModel, message); } public static CommonResponse MissingContentType() { - return new CommonResponse(ErrorCodes.Header_Missing_ContentType, "Header Content-Type is required."); + return new CommonResponse(ErrorCodes.Http.Common.Header.Missing_ContentType, "Header Content-Type is required."); } public static CommonResponse MissingContentLength() { - return new CommonResponse(ErrorCodes.Header_Missing_ContentLength, "Header Content-Length is missing or of bad format."); + return new CommonResponse(ErrorCodes.Http.Common.Header.Missing_ContentLength, "Header Content-Length is missing or of bad format."); } public static CommonResponse ZeroContentLength() { - return new CommonResponse(ErrorCodes.Header_Zero_ContentLength, "Header Content-Length must not be 0."); + return new CommonResponse(ErrorCodes.Http.Common.Header.Zero_ContentLength, "Header Content-Length must not be 0."); } public static CommonResponse BadIfNonMatch() { - return new CommonResponse(ErrorCodes.Header_BadFormat_IfNonMatch, "Header If-Non-Match is of bad format."); + return new CommonResponse(ErrorCodes.Http.Common.Header.BadFormat_IfNonMatch, "Header If-Non-Match is of bad format."); } public CommonResponse() @@ -56,21 +42,55 @@ namespace Timeline.Models.Http public string Message { get; set; } } + public class CommonDataResponse : CommonResponse + { + public CommonDataResponse() + { + + } + + public CommonDataResponse(int code, string message, T data) + : base(code, message) + { + Data = data; + } + + public T Data { get; set; } + } + public static class CommonPutResponse { - public const int CreatedCode = 0; - public const int ModifiedCode = 1; + public class ResponseData + { + public ResponseData(bool create) + { + Create = create; + } - public static CommonResponse Created { get; } = new CommonResponse(CreatedCode, "A new item is created."); - public static CommonResponse Modified { get; } = new CommonResponse(ModifiedCode, "An existent item is modified."); + public bool Create { get; set; } + } + + public static CommonDataResponse Create() => + new CommonDataResponse(0, "A new item is created.", new ResponseData(true)); + public static CommonDataResponse Modify() => + new CommonDataResponse(0, "An existent item is modified.", new ResponseData(false)); } public static class CommonDeleteResponse { - public const int DeletedCode = 0; - public const int NotExistsCode = 1; + public class ResponseData + { + public ResponseData(bool delete) + { + Delete = delete; + } + + public bool Delete { get; set; } + } - public static CommonResponse Deleted { get; } = new CommonResponse(DeletedCode, "An existent item is deleted."); - public static CommonResponse NotExists { get; } = new CommonResponse(NotExistsCode, "The item does not exist."); + public static CommonDataResponse Delete() => + new CommonDataResponse(0, "An existent item is deleted.", new ResponseData(true)); + public static CommonDataResponse NotExist() => + new CommonDataResponse(0, "The item does not exist.", new ResponseData(false)); } } diff --git a/Timeline/Models/Http/Token.cs b/Timeline/Models/Http/Token.cs index 68a66d0a..615b6d8a 100644 --- a/Timeline/Models/Http/Token.cs +++ b/Timeline/Models/Http/Token.cs @@ -10,7 +10,7 @@ namespace Timeline.Models.Http public string Password { get; set; } // in days, optional [Range(1, 365)] - public int? ExpireOffset { get; set; } + public int? Expire { get; set; } } public class CreateTokenResponse -- cgit v1.2.3