aboutsummaryrefslogtreecommitdiff
path: root/Timeline/Controllers
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2019-10-24 20:15:58 +0800
committerGitHub <noreply@github.com>2019-10-24 20:15:58 +0800
commit7305358a88ffc87f51f7b78deb4f07ef99120beb (patch)
tree7ca5010a06829cc5fadea1ea17ae72d082fc344c /Timeline/Controllers
parent297d0c9029360f1d5334ed843b9b299356740ec1 (diff)
parenta0f3cd7599a48c14fb5492fb1c6e2dbd0a82fb45 (diff)
downloadtimeline-7305358a88ffc87f51f7b78deb4f07ef99120beb.tar.gz
timeline-7305358a88ffc87f51f7b78deb4f07ef99120beb.tar.bz2
timeline-7305358a88ffc87f51f7b78deb4f07ef99120beb.zip
Merge pull request #50 from crupest/refactor
Refactor : A Huge Step
Diffstat (limited to 'Timeline/Controllers')
-rw-r--r--Timeline/Controllers/Testing/TestingAuthController.cs (renamed from Timeline/Controllers/UserTestController.cs)8
-rw-r--r--Timeline/Controllers/TokenController.cs161
-rw-r--r--Timeline/Controllers/UserAvatarController.cs141
-rw-r--r--Timeline/Controllers/UserController.cs143
-rw-r--r--Timeline/Controllers/UserDetailController.cs96
5 files changed, 255 insertions, 294 deletions
diff --git a/Timeline/Controllers/UserTestController.cs b/Timeline/Controllers/Testing/TestingAuthController.cs
index 2a5f36a1..67b5b2ef 100644
--- a/Timeline/Controllers/UserTestController.cs
+++ b/Timeline/Controllers/Testing/TestingAuthController.cs
@@ -1,12 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using Timeline.Authenticate;
+using Timeline.Authentication;
-namespace Timeline.Controllers
+namespace Timeline.Controllers.Testing
{
- [Route("Test/User")]
+ [Route("testing/auth")]
[ApiController]
- public class UserTestController : Controller
+ public class TestingAuthController : Controller
{
[HttpGet("[action]")]
[Authorize]
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs
index 3c166448..4e32d26f 100644
--- a/Timeline/Controllers/TokenController.cs
+++ b/Timeline/Controllers/TokenController.cs
@@ -3,71 +3,84 @@ 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;
+using Microsoft.Extensions.Localization;
+using System.Globalization;
+using static Timeline.Resources.Controllers.TokenController;
-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<TokenController> _logger;
private readonly IClock _clock;
+ private readonly IStringLocalizer<TokenController> _localizer;
- public TokenController(IUserService userService, ILogger<TokenController> logger, IClock clock)
+ public TokenController(IUserService userService, ILogger<TokenController> logger, IClock clock, IStringLocalizer<TokenController> localizer)
{
_userService = userService;
_logger = logger;
_clock = clock;
+ _localizer = localizer;
}
[HttpPost("create")]
[AllowAnonymous]
- public async Task<IActionResult> Create([FromBody] CreateTokenRequest request)
+ public async Task<ActionResult<CreateTokenResponse>> 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(LogCreateFailure,
+ ("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(LogCreateSuccess,
+ ("Username", request.Username),
+ ("Expire At", expireTime?.ToString(CultureInfo.CurrentUICulture.DateTimeFormat) ?? "default")
+ ));
return Ok(new CreateTokenResponse
{
Token = result.Token,
@@ -76,75 +89,71 @@ 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(LogUserNotExist, e);
+ return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Create.BadCredential,
+ _localizer["ErrorBadCredential"]));
}
catch (BadPasswordException e)
{
- var code = ErrorCodes.Create_BadPassword;
- LogFailure("Password is wrong.", code, e);
- return BadRequest(new CommonResponse(code, "Bad username or password."));
+ LogFailure(LogBadPassword, e);
+ return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Create.BadCredential,
+ _localizer["ErrorBadCredential"]));
}
}
[HttpPost("verify")]
[AllowAnonymous]
- public async Task<IActionResult> Verify([FromBody] VerifyTokenRequest request)
+ public async Task<ActionResult<VerifyTokenResponse>> Verify([FromBody] VerifyTokenRequest request)
{
- void LogFailure(string reason, int code, Exception e = null, params KeyValuePair<string, object>[] otherProperties)
+ void LogFailure(string reason, Exception? e = null, params (string, object?)[] otherProperties)
{
- var properties = new KeyValuePair<string, object>[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(LogVerifyFailure, 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(LogVerifySuccess,
+ ("Username", result.Username), ("Token", request.Token)));
return Ok(new VerifyTokenResponse
{
User = result
});
}
- catch (JwtTokenVerifyException e)
+ catch (JwtVerifyException e)
{
- if (e.ErrorCode == JwtTokenVerifyException.ErrorCodes.Expired)
+ if (e.ErrorCode == JwtVerifyException.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(LogVerifyExpire, e, ("Expires", innerException?.Expires),
+ ("Current Time", _clock.GetCurrentTime()));
+ return BadRequest(new CommonResponse(
+ ErrorCodes.Http.Token.Verify.Expired, _localizer["ErrorVerifyExpire"]));
+ }
+ else if (e.ErrorCode == JwtVerifyException.ErrorCodes.OldVersion)
+ {
+ var innerException = e.InnerException as JwtBadVersionException;
+ LogFailure(LogVerifyOldVersion, e,
+ ("Token Version", innerException?.TokenVersion), ("Required Version", innerException?.RequiredVersion));
+ return BadRequest(new CommonResponse(
+ ErrorCodes.Http.Token.Verify.OldVersion, _localizer["ErrorVerifyOldVersion"]));
}
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(LogVerifyBadFormat, e);
+ return BadRequest(new CommonResponse(
+ ErrorCodes.Http.Token.Verify.BadFormat, _localizer["ErrorVerifyBadFormat"]));
}
}
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));
- }
- 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));
+ LogFailure(LogVerifyUserNotExist, e);
+ return BadRequest(new CommonResponse(
+ ErrorCodes.Http.Token.Verify.UserNotExist, _localizer["ErrorVerifyUserNotExist"]));
}
}
}
diff --git a/Timeline/Controllers/UserAvatarController.cs b/Timeline/Controllers/UserAvatarController.cs
index e77076ca..838a3928 100644
--- a/Timeline/Controllers/UserAvatarController.cs
+++ b/Timeline/Controllers/UserAvatarController.cs
@@ -1,68 +1,75 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using System;
using System.Linq;
using System.Threading.Tasks;
-using Timeline.Authenticate;
+using Timeline.Authentication;
using Timeline.Filters;
+using Timeline.Helpers;
using Timeline.Models.Http;
+using Timeline.Models.Validation;
using Timeline.Services;
-namespace Timeline.Controllers
+namespace Timeline
{
- [ApiController]
- public class UserAvatarController : Controller
+ public static partial class ErrorCodes
{
- public static class ErrorCodes
+ public static partial class Http
{
- public const int Get_UserNotExist = -1001;
-
- 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 Put_Content_TooBig = -2021;
- public const int Put_Content_UnmatchedLength_Less = -2022;
- public const int Put_Content_UnmatchedLength_Bigger = -2023;
+ public static class UserAvatar // bbb = 003
+ {
+ public static class Get // cc = 01
+ {
+ public const int UserNotExist = 10030101;
+ }
- public const int Delete_UserNotExist = -3001;
- public const int Delete_Forbid = -3002;
+ public static class Put // cc = 02
+ {
+ public const int UserNotExist = 10030201;
+ public const int Forbid = 10030202;
+ public const int BadFormat_CantDecode = 10030203;
+ public const int BadFormat_UnmatchedFormat = 10030204;
+ public const int BadFormat_BadSize = 10030205;
+ }
- public static int From(AvatarDataException.ErrorReason error)
- {
- switch (error)
+ public static class Delete // cc = 03
{
- 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.");
+ public const int UserNotExist = 10030301;
+ public const int Forbid = 10030302;
}
}
}
+ }
+}
+namespace Timeline.Controllers
+{
+ [ApiController]
+ public class UserAvatarController : Controller
+ {
private readonly ILogger<UserAvatarController> _logger;
private readonly IUserAvatarService _service;
- public UserAvatarController(ILogger<UserAvatarController> logger, IUserAvatarService service)
+ private readonly IStringLocalizerFactory _localizerFactory;
+ private readonly IStringLocalizer<UserAvatarController> _localizer;
+
+ public UserAvatarController(ILogger<UserAvatarController> logger, IUserAvatarService service, IStringLocalizerFactory localizerFactory)
{
_logger = logger;
_service = service;
+ _localizerFactory = localizerFactory;
+ _localizer = new StringLocalizer<UserAvatarController>(localizerFactory);
}
[HttpGet("users/{username}/avatar")]
- [Authorize]
[ResponseCache(NoStore = false, Location = ResponseCacheLocation.None, Duration = 0)]
- public async Task<IActionResult> Get([FromRoute] string username)
+ public async Task<IActionResult> Get([FromRoute][Username] string username)
{
const string IfNonMatchHeaderKey = "If-None-Match";
@@ -74,11 +81,16 @@ namespace Timeline.Controllers
if (Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var value))
{
if (!EntityTagHeaderValue.TryParseStrictList(value, out var eTagList))
- return BadRequest(CommonResponse.BadIfNonMatch());
+ {
+ _logger.LogInformation(Log.Format(Resources.Controllers.UserAvatarController.LogGetBadIfNoneMatch,
+ ("Username", username), ("If-None-Match", value)));
+ return BadRequest(HeaderErrorResponse.BadIfNonMatch(_localizerFactory));
+ }
if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
{
Response.Headers.Add("ETag", eTagValue);
+ _logger.LogInformation(Log.Format(Resources.Controllers.UserAvatarController.LogGetReturnNotModify, ("Username", username)));
return StatusCode(StatusCodes.Status304NotModified);
}
}
@@ -86,12 +98,13 @@ namespace Timeline.Controllers
var avatarInfo = await _service.GetAvatar(username);
var avatar = avatarInfo.Avatar;
+ _logger.LogInformation(Log.Format(Resources.Controllers.UserAvatarController.LogGetReturnData, ("Username", username)));
return File(avatar.Data, avatar.Type, new DateTimeOffset(avatarInfo.LastModified), eTag);
}
catch (UserNotExistException e)
{
- _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."));
+ _logger.LogInformation(e, Log.Format(Resources.Controllers.UserAvatarController.LogGetUserNotExist, ("Username", username)));
+ return NotFound(new CommonResponse(ErrorCodes.Http.UserAvatar.Get.UserNotExist, _localizer["ErrorGetUserNotExist"]));
}
}
@@ -99,18 +112,18 @@ namespace Timeline.Controllers
[Authorize]
[RequireContentType, RequireContentLength]
[Consumes("image/png", "image/jpeg", "image/gif", "image/webp")]
- public async Task<IActionResult> Put(string username)
+ public async Task<IActionResult> Put([FromRoute][Username] string username)
{
- var contentLength = Request.ContentLength.Value;
+ var contentLength = Request.ContentLength!.Value;
if (contentLength > 1000 * 1000 * 10)
- return BadRequest(new CommonResponse(ErrorCodes.Put_Content_TooBig,
- "Content can't be bigger than 10MB."));
+ return BadRequest(ContentErrorResponse.TooBig(_localizerFactory, "10MB"));
- if (!User.IsAdmin() && User.Identity.Name != username)
+ if (!User.IsAdministrator() && User.Identity.Name != username)
{
- _logger.LogInformation($"Attempt to put a avatar of other user as a non-admin failed. Operator Username: {User.Identity.Name} ; Username To Put Avatar: {username} .");
+ _logger.LogInformation(Log.Format(Resources.Controllers.UserAvatarController.LogPutForbid,
+ ("Operator Username", User.Identity.Name), ("Username To Put Avatar", username)));
return StatusCode(StatusCodes.Status403Forbidden,
- new CommonResponse(ErrorCodes.Put_Forbid, "Normal user can't change other's avatar."));
+ new CommonResponse(ErrorCodes.Http.UserAvatar.Put.Forbid, _localizer["ErrorPutForbid"]));
}
try
@@ -119,13 +132,11 @@ namespace Timeline.Controllers
var bytesRead = await Request.Body.ReadAsync(data);
if (bytesRead != contentLength)
- return BadRequest(new CommonResponse(ErrorCodes.Put_Content_UnmatchedLength_Less,
- $"Content length in header is {contentLength} but actual length is {bytesRead}."));
+ return BadRequest(ContentErrorResponse.UnmatchedLength_Smaller(_localizerFactory));
var extraByte = new byte[1];
if (await Request.Body.ReadAsync(extraByte) != 0)
- return BadRequest(new CommonResponse(ErrorCodes.Put_Content_UnmatchedLength_Bigger,
- $"Content length in header is {contentLength} but actual length is bigger than that."));
+ return BadRequest(ContentErrorResponse.UnmatchedLength_Bigger(_localizerFactory));
await _service.SetAvatar(username, new Avatar
{
@@ -133,43 +144,57 @@ namespace Timeline.Controllers
Type = Request.ContentType
});
- _logger.LogInformation($"Succeed to put a avatar of a user. Username: {username} ; Mime Type: {Request.ContentType} .");
+ _logger.LogInformation(Log.Format(Resources.Controllers.UserAvatarController.LogPutSuccess,
+ ("Username", username), ("Mime Type", Request.ContentType)));
return Ok();
}
catch (UserNotExistException e)
{
- _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."));
+ _logger.LogInformation(e, Log.Format(Resources.Controllers.UserAvatarController.LogPutUserNotExist, ("Username", username)));
+ return BadRequest(new CommonResponse(ErrorCodes.Http.UserAvatar.Put.UserNotExist, _localizer["ErrorPutUserNotExist"]));
}
- catch (AvatarDataException e)
+ catch (AvatarFormatException 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."));
+ var (code, message) = e.Error switch
+ {
+ AvatarFormatException.ErrorReason.CantDecode =>
+ (ErrorCodes.Http.UserAvatar.Put.BadFormat_CantDecode, _localizer["ErrorPutBadFormatCantDecode"]),
+ AvatarFormatException.ErrorReason.UnmatchedFormat =>
+ (ErrorCodes.Http.UserAvatar.Put.BadFormat_UnmatchedFormat, _localizer["ErrorPutBadFormatUnmatchedFormat"]),
+ AvatarFormatException.ErrorReason.BadSize =>
+ (ErrorCodes.Http.UserAvatar.Put.BadFormat_BadSize, _localizer["ErrorPutBadFormatBadSize"]),
+ _ =>
+ throw new Exception(Resources.Controllers.UserAvatarController.ExceptionUnknownAvatarFormatError)
+ };
+
+ _logger.LogInformation(e, Log.Format(Resources.Controllers.UserAvatarController.LogPutUserBadFormat, ("Username", username)));
+ return BadRequest(new CommonResponse(code, message));
}
}
[HttpDelete("users/{username}/avatar")]
[Authorize]
- public async Task<IActionResult> Delete([FromRoute] string username)
+ public async Task<IActionResult> Delete([FromRoute][Username] string username)
{
- if (!User.IsAdmin() && User.Identity.Name != username)
+ if (!User.IsAdministrator() && User.Identity.Name != username)
{
- _logger.LogInformation($"Attempt to delete a avatar of other user as a non-admin failed. Operator Username: {User.Identity.Name} ; Username To Put Avatar: {username} .");
+ _logger.LogInformation(Log.Format(Resources.Controllers.UserAvatarController.LogPutUserBadFormat,
+ ("Operator Username", User.Identity.Name), ("Username To Delete Avatar", username)));
return StatusCode(StatusCodes.Status403Forbidden,
- new CommonResponse(ErrorCodes.Delete_Forbid, "Normal user can't delete other's avatar."));
+ new CommonResponse(ErrorCodes.Http.UserAvatar.Delete.Forbid, _localizer["ErrorDeleteForbid"]));
}
try
{
await _service.SetAvatar(username, null);
- _logger.LogInformation($"Succeed to delete a avatar of a user. Username: {username} .");
+ _logger.LogInformation(Log.Format(Resources.Controllers.UserAvatarController.LogDeleteSuccess, ("Username", username)));
return Ok();
}
catch (UserNotExistException e)
{
- _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."));
+ _logger.LogInformation(e, Log.Format(Resources.Controllers.UserAvatarController.LogDeleteNotExist, ("Username", username)));
+ return BadRequest(new CommonResponse(ErrorCodes.Http.UserAvatar.Delete.UserNotExist, _localizer["ErrorDeleteUserNotExist"]));
}
}
}
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs
index bd13f0a3..1771dc85 100644
--- a/Timeline/Controllers/UserController.cs
+++ b/Timeline/Controllers/UserController.cs
@@ -1,40 +1,70 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
-using System;
using System.Threading.Tasks;
-using Timeline.Authenticate;
+using Timeline.Authentication;
+using Timeline.Helpers;
using Timeline.Models;
using Timeline.Models.Http;
+using Timeline.Models.Validation;
using Timeline.Services;
-using static Timeline.Helpers.MyLogHelper;
+using static Timeline.Resources.Controllers.UserController;
-namespace Timeline.Controllers
+namespace Timeline
{
- [ApiController]
- public class UserController : Controller
+ public static partial class ErrorCodes
{
- public static class ErrorCodes
+ public static partial class Http
{
- public const int Get_NotExist = -1001;
+ public static class User // bbb = 002
+ {
+ public static class Get // cc = 01
+ {
+ public const int NotExist = 10020101; // dd = 01
+ }
- public const int Put_BadUsername = -2001;
+ public static class Patch // cc = 03
+ {
+ public const int NotExist = 10020301; // dd = 01
+ }
- public const int Patch_NotExist = -3001;
+ public static class Op // cc = 1x
+ {
+ public static class ChangeUsername // cc = 11
+ {
+ public const int NotExist = 10021101; // dd = 01
+ public const int AlreadyExist = 10021102; // dd = 02
+ }
- public const int ChangeUsername_NotExist = -4001;
- public const int ChangeUsername_AlreadyExist = -4002;
+ public static class ChangePassword // cc = 12
+ {
+ public const int BadOldPassword = 10021201; // dd = 01
+ }
+ }
- public const int ChangePassword_BadOldPassword = -5001;
+ }
}
+ }
+}
+
+namespace Timeline.Controllers
+{
+ [ApiController]
+ public class UserController : Controller
+ {
private readonly ILogger<UserController> _logger;
private readonly IUserService _userService;
+ private readonly IStringLocalizerFactory _localizerFactory;
+ private readonly IStringLocalizer _localizer;
- public UserController(ILogger<UserController> logger, IUserService userService)
+ public UserController(ILogger<UserController> logger, IUserService userService, IStringLocalizerFactory localizerFactory)
{
_logger = logger;
_userService = userService;
+ _localizerFactory = localizerFactory;
+ _localizer = localizerFactory.Create(GetType());
}
[HttpGet("users"), AdminAuthorize]
@@ -44,44 +74,36 @@ namespace Timeline.Controllers
}
[HttpGet("users/{username}"), AdminAuthorize]
- public async Task<IActionResult> Get([FromRoute] string username)
+ public async Task<ActionResult<UserInfo>> Get([FromRoute][Username] string username)
{
var user = await _userService.GetUser(username);
if (user == null)
{
- _logger.LogInformation(FormatLogMessage("Attempt to get a non-existent user.", Pair("Username", username)));
- return NotFound(new CommonResponse(ErrorCodes.Get_NotExist, "The user does not exist."));
+ _logger.LogInformation(Log.Format(LogGetUserNotExist, ("Username", username)));
+ return NotFound(new CommonResponse(ErrorCodes.Http.User.Get.NotExist, _localizer["ErrorGetUserNotExist"]));
}
return Ok(user);
}
[HttpPut("users/{username}"), AdminAuthorize]
- public async Task<IActionResult> Put([FromBody] UserPutRequest request, [FromRoute] string username)
+ public async Task<ActionResult<CommonPutResponse>> Put([FromBody] UserPutRequest request, [FromRoute][Username] string username)
{
- try
- {
- var result = await _userService.PutUser(username, request.Password, request.Administrator.Value);
- switch (result)
- {
- case PutResult.Created:
- _logger.LogInformation(FormatLogMessage("A user is created.", Pair("Username", username)));
- return CreatedAtAction("Get", new { username }, CommonPutResponse.Created);
- case PutResult.Modified:
- _logger.LogInformation(FormatLogMessage("A user is modified.", Pair("Username", username)));
- return Ok(CommonPutResponse.Modified);
- default:
- throw new Exception("Unreachable code.");
- }
- }
- catch (UsernameBadFormatException e)
+ var result = await _userService.PutUser(username, request.Password, request.Administrator!.Value);
+ switch (result)
{
- _logger.LogInformation(e, FormatLogMessage("Attempt to create a user with bad username failed.", Pair("Username", username)));
- return BadRequest(new CommonResponse(ErrorCodes.Put_BadUsername, "Username is of bad format."));
+ case PutResult.Create:
+ _logger.LogInformation(Log.Format(LogPutCreate, ("Username", username)));
+ return CreatedAtAction("Get", new { username }, CommonPutResponse.Create(_localizerFactory));
+ case PutResult.Modify:
+ _logger.LogInformation(Log.Format(LogPutModify, ("Username", username)));
+ return Ok(CommonPutResponse.Modify(_localizerFactory));
+ default:
+ throw new InvalidBranchException();
}
}
[HttpPatch("users/{username}"), AdminAuthorize]
- public async Task<IActionResult> Patch([FromBody] UserPatchRequest request, [FromRoute] string username)
+ public async Task<ActionResult> Patch([FromBody] UserPatchRequest request, [FromRoute][Username] string username)
{
try
{
@@ -90,66 +112,67 @@ namespace Timeline.Controllers
}
catch (UserNotExistException e)
{
- _logger.LogInformation(e, FormatLogMessage("Attempt to patch a non-existent user.", Pair("Username", username)));
- return NotFound(new CommonResponse(ErrorCodes.Patch_NotExist, "The user does not exist."));
+ _logger.LogInformation(e, Log.Format(LogPatchUserNotExist, ("Username", username)));
+ return NotFound(new CommonResponse(ErrorCodes.Http.User.Patch.NotExist, _localizer["ErrorPatchUserNotExist"]));
}
}
[HttpDelete("users/{username}"), AdminAuthorize]
- public async Task<IActionResult> Delete([FromRoute] string username)
+ public async Task<ActionResult<CommonDeleteResponse>> Delete([FromRoute][Username] string username)
{
try
{
await _userService.DeleteUser(username);
- _logger.LogInformation(FormatLogMessage("A user is deleted.", Pair("Username", username)));
- return Ok(CommonDeleteResponse.Deleted);
+ _logger.LogInformation(Log.Format(LogDeleteDelete, ("Username", username)));
+ return Ok(CommonDeleteResponse.Delete(_localizerFactory));
}
catch (UserNotExistException e)
{
- _logger.LogInformation(e, FormatLogMessage("Attempt to delete a non-existent user.", Pair("Username", username)));
- return Ok(CommonDeleteResponse.NotExists);
+ _logger.LogInformation(e, Log.Format(LogDeleteNotExist, ("Username", username)));
+ return Ok(CommonDeleteResponse.NotExist(_localizerFactory));
}
}
[HttpPost("userop/changeusername"), AdminAuthorize]
- public async Task<IActionResult> ChangeUsername([FromBody] ChangeUsernameRequest request)
+ public async Task<ActionResult> ChangeUsername([FromBody] ChangeUsernameRequest request)
{
try
{
await _userService.ChangeUsername(request.OldUsername, request.NewUsername);
- _logger.LogInformation(FormatLogMessage("A user changed username.",
- Pair("Old Username", request.OldUsername), Pair("New Username", request.NewUsername)));
+ _logger.LogInformation(Log.Format(LogChangeUsernameSuccess,
+ ("Old Username", request.OldUsername), ("New Username", request.NewUsername)));
return Ok();
}
catch (UserNotExistException e)
{
- _logger.LogInformation(e, FormatLogMessage("Attempt to change a non-existent user's username failed.",
- Pair("Old Username", request.OldUsername), Pair("New Username", request.NewUsername)));
- return BadRequest(new CommonResponse(ErrorCodes.ChangeUsername_NotExist, $"The user {request.OldUsername} does not exist."));
+ _logger.LogInformation(e, Log.Format(LogChangeUsernameNotExist,
+ ("Old Username", request.OldUsername), ("New Username", request.NewUsername)));
+ return BadRequest(new CommonResponse(ErrorCodes.Http.User.Op.ChangeUsername.NotExist, _localizer["ErrorChangeUsernameNotExist", request.OldUsername]));
}
- catch (UserAlreadyExistException e)
+ catch (UsernameConfictException e)
{
- _logger.LogInformation(e, FormatLogMessage("Attempt to change a user's username to a existent one failed.",
- Pair("Old Username", request.OldUsername), Pair("New Username", request.NewUsername)));
- return BadRequest(new CommonResponse(ErrorCodes.ChangeUsername_AlreadyExist, $"The user {request.NewUsername} already exists."));
+ _logger.LogInformation(e, Log.Format(LogChangeUsernameAlreadyExist,
+ ("Old Username", request.OldUsername), ("New Username", request.NewUsername)));
+ return BadRequest(new CommonResponse(ErrorCodes.Http.User.Op.ChangeUsername.AlreadyExist, _localizer["ErrorChangeUsernameAlreadyExist"]));
}
// there is no need to catch bad format exception because it is already checked in model validation.
}
[HttpPost("userop/changepassword"), Authorize]
- public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordRequest request)
+ public async Task<ActionResult> ChangePassword([FromBody] ChangePasswordRequest request)
{
try
{
- await _userService.ChangePassword(User.Identity.Name, request.OldPassword, request.NewPassword);
- _logger.LogInformation(FormatLogMessage("A user changed password.", Pair("Username", User.Identity.Name)));
+ await _userService.ChangePassword(User.Identity.Name!, request.OldPassword, request.NewPassword);
+ _logger.LogInformation(Log.Format(LogChangePasswordSuccess, ("Username", User.Identity.Name)));
return Ok();
}
catch (BadPasswordException e)
{
- _logger.LogInformation(e, FormatLogMessage("A user attempt to change password but old password is wrong.",
- Pair("Username", User.Identity.Name), Pair("Old Password", request.OldPassword)));
- return BadRequest(new CommonResponse(ErrorCodes.ChangePassword_BadOldPassword, "Old password is wrong."));
+ _logger.LogInformation(e, Log.Format(LogChangePasswordBadPassword,
+ ("Username", User.Identity.Name), ("Old Password", request.OldPassword)));
+ return BadRequest(new CommonResponse(ErrorCodes.Http.User.Op.ChangePassword.BadOldPassword,
+ _localizer["ErrorChangePasswordBadPassword"]));
}
// User can't be non-existent or the token is bad.
}
diff --git a/Timeline/Controllers/UserDetailController.cs b/Timeline/Controllers/UserDetailController.cs
deleted file mode 100644
index 5e1183c1..00000000
--- a/Timeline/Controllers/UserDetailController.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using System.Threading.Tasks;
-using Timeline.Authenticate;
-using Timeline.Models;
-using Timeline.Models.Http;
-using Timeline.Services;
-
-namespace Timeline.Controllers
-{
- [Route("users/{username}")]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- [ApiController]
- public class UserDetailController : Controller
- {
- public static class ErrorCodes
- {
- public const int Get_UserNotExist = -1001;
-
- public const int Patch_Forbid = -2001;
- public const int Patch_UserNotExist = -2002;
-
- public const int GetNickname_UserNotExist = -3001;
- }
-
- private readonly ILogger<UserDetailController> _logger;
- private readonly IUserDetailService _service;
-
- public UserDetailController(ILogger<UserDetailController> logger, IUserDetailService service)
- {
- _logger = logger;
- _service = service;
- }
-
- [HttpGet("nickname")]
- [UserAuthorize]
- [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserDetail))]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> GetNickname([FromRoute] string username)
- {
- try
- {
- var nickname = await _service.GetUserNickname(username);
- return Ok(new UserDetail
- {
- Nickname = nickname
- });
- }
- catch (UserNotExistException)
- {
- return NotFound(new CommonResponse(ErrorCodes.GetNickname_UserNotExist, "The user does not exist."));
- }
- }
-
- [HttpGet("details")]
- [UserAuthorize]
- [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UserDetail))]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> Get([FromRoute] string username)
- {
- try
- {
- var detail = await _service.GetUserDetail(username);
- return Ok(detail);
- }
- catch (UserNotExistException)
- {
- return NotFound(new CommonResponse(ErrorCodes.Get_UserNotExist, "The user does not exist."));
- }
- }
-
- [HttpPatch("details")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(void))]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> Patch([FromRoute] string username, [FromBody] UserDetail detail)
- {
- if (!User.IsAdmin() && User.Identity.Name != username)
- return StatusCode(StatusCodes.Status403Forbidden, new CommonResponse(ErrorCodes.Patch_Forbid, "You can't change other's details unless you are admin."));
-
- try
- {
- await _service.UpdateUserDetail(username, detail);
- return Ok();
- }
- catch (UserNotExistException)
- {
- return NotFound(new CommonResponse(ErrorCodes.Patch_UserNotExist, "The user does not exist."));
- }
- }
- }
-}