aboutsummaryrefslogtreecommitdiff
path: root/Timeline/Controllers
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2020-02-01 00:26:35 +0800
committerGitHub <noreply@github.com>2020-02-01 00:26:35 +0800
commitd703269e06d4c9e254fe2d5589ff04cdd6a9b366 (patch)
treef02f8d57440c777d4732bc4439f82e8b25c6732c /Timeline/Controllers
parent631731e5c2253116a53fdc435afca184251a34fc (diff)
parentbddf1d6eaac782672071df6527c40c81c3123f3a (diff)
downloadtimeline-d703269e06d4c9e254fe2d5589ff04cdd6a9b366.tar.gz
timeline-d703269e06d4c9e254fe2d5589ff04cdd6a9b366.tar.bz2
timeline-d703269e06d4c9e254fe2d5589ff04cdd6a9b366.zip
Merge pull request #56 from crupest/dev
Refactor API to be RESTful.
Diffstat (limited to 'Timeline/Controllers')
-rw-r--r--Timeline/Controllers/ControllerAuthExtensions.cs40
-rw-r--r--Timeline/Controllers/PersonalTimelineController.cs143
-rw-r--r--Timeline/Controllers/Testing/TestingI18nController.cs36
-rw-r--r--Timeline/Controllers/TokenController.cs92
-rw-r--r--Timeline/Controllers/UserAvatarController.cs155
-rw-r--r--Timeline/Controllers/UserController.cs172
-rw-r--r--Timeline/Controllers/UserDetailController.cs49
7 files changed, 253 insertions, 434 deletions
diff --git a/Timeline/Controllers/ControllerAuthExtensions.cs b/Timeline/Controllers/ControllerAuthExtensions.cs
new file mode 100644
index 00000000..00a65454
--- /dev/null
+++ b/Timeline/Controllers/ControllerAuthExtensions.cs
@@ -0,0 +1,40 @@
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Security.Claims;
+using Timeline.Auth;
+using static Timeline.Resources.Controllers.ControllerAuthExtensions;
+
+namespace Timeline.Controllers
+{
+ public static class ControllerAuthExtensions
+ {
+ public static bool IsAdministrator(this ControllerBase controller)
+ {
+ return controller.User != null && controller.User.IsAdministrator();
+ }
+
+ public static long GetUserId(this ControllerBase controller)
+ {
+ var claim = controller.User.FindFirst(ClaimTypes.NameIdentifier);
+ if (claim == null)
+ throw new InvalidOperationException(ExceptionNoUserIdentifierClaim);
+
+ if (long.TryParse(claim.Value, out var value))
+ return value;
+
+ throw new InvalidOperationException(ExceptionUserIdentifierClaimBadFormat);
+ }
+
+ public static long? GetOptionalUserId(this ControllerBase controller)
+ {
+ var claim = controller.User.FindFirst(ClaimTypes.NameIdentifier);
+ if (claim == null)
+ return null;
+
+ if (long.TryParse(claim.Value, out var value))
+ return value;
+
+ throw new InvalidOperationException(ExceptionUserIdentifierClaimBadFormat);
+ }
+ }
+}
diff --git a/Timeline/Controllers/PersonalTimelineController.cs b/Timeline/Controllers/PersonalTimelineController.cs
index c864ed39..11353bb5 100644
--- a/Timeline/Controllers/PersonalTimelineController.cs
+++ b/Timeline/Controllers/PersonalTimelineController.cs
@@ -3,64 +3,22 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
-using System.Globalization;
using System.Threading.Tasks;
-using Timeline.Auth;
using Timeline.Filters;
-using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Models.Validation;
using Timeline.Services;
-using static Timeline.Resources.Controllers.TimelineController;
-
-namespace Timeline
-{
- public static partial class ErrorCodes
- {
- public static partial class Http
- {
- public static class Timeline // ccc = 004
- {
- public const int PostListGetForbid = 10040101;
- public const int PostOperationCreateForbid = 10040102;
- public const int PostOperationDeleteForbid = 10040103;
- public const int PostOperationDeleteNotExist = 10040201;
- public const int ChangeMemberUserNotExist = 10040301;
- }
- }
- }
-}
namespace Timeline.Controllers
{
[ApiController]
+ [CatchTimelineNotExistException]
public class PersonalTimelineController : Controller
{
private readonly ILogger<PersonalTimelineController> _logger;
private readonly IPersonalTimelineService _service;
- private bool IsAdmin()
- {
- if (User != null)
- {
- return User.IsAdministrator();
- }
- return false;
- }
-
- private string? GetAuthUsername()
- {
- if (User == null)
- {
- return null;
- }
- else
- {
- return User.Identity.Name;
- }
- }
-
public PersonalTimelineController(ILogger<PersonalTimelineController> logger, IPersonalTimelineService service)
{
_logger = logger;
@@ -68,100 +26,105 @@ namespace Timeline.Controllers
}
[HttpGet("users/{username}/timeline")]
- [CatchTimelineNotExistException]
public async Task<ActionResult<BaseTimelineInfo>> TimelineGet([FromRoute][Username] string username)
{
return await _service.GetTimeline(username);
}
[HttpGet("users/{username}/timeline/posts")]
- [CatchTimelineNotExistException]
public async Task<ActionResult<IList<TimelinePostInfo>>> PostListGet([FromRoute][Username] string username)
{
- if (!IsAdmin() && !await _service.HasReadPermission(username, GetAuthUsername()))
+ if (!this.IsAdministrator() && !await _service.HasReadPermission(username, this.GetOptionalUserId()))
{
- return StatusCode(StatusCodes.Status403Forbidden,
- new CommonResponse(ErrorCodes.Http.Timeline.PostListGetForbid, MessagePostListGetForbid));
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
return await _service.GetPosts(username);
}
- [HttpPost("users/{username}/timeline/postop/create")]
+ [HttpPost("users/{username}/timeline/posts")]
[Authorize]
- [CatchTimelineNotExistException]
- public async Task<ActionResult<TimelinePostCreateResponse>> PostOperationCreate([FromRoute][Username] string username, [FromBody] TimelinePostCreateRequest body)
+ public async Task<ActionResult<TimelinePostInfo>> PostPost([FromRoute][Username] string username, [FromBody] TimelinePostCreateRequest body)
{
- if (!IsAdmin() && !await _service.IsMemberOf(username, GetAuthUsername()!))
+ var id = this.GetUserId();
+ if (!this.IsAdministrator() && !await _service.IsMemberOf(username, id))
{
- return StatusCode(StatusCodes.Status403Forbidden,
- new CommonResponse(ErrorCodes.Http.Timeline.PostOperationCreateForbid, MessagePostOperationCreateForbid));
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
- var res = await _service.CreatePost(username, User.Identity.Name!, body.Content, body.Time);
+ var res = await _service.CreatePost(username, id, body.Content, body.Time);
return res;
}
- [HttpPost("users/{username}/timeline/postop/delete")]
+ [HttpDelete("users/{username}/timeline/posts/{id}")]
[Authorize]
- [CatchTimelineNotExistException]
- public async Task<ActionResult> PostOperationDelete([FromRoute][Username] string username, [FromBody] TimelinePostDeleteRequest body)
+ public async Task<ActionResult> PostDelete([FromRoute][Username] string username, [FromRoute] long id)
{
try
{
- var postId = body.Id!.Value;
- if (!IsAdmin() && !await _service.HasPostModifyPermission(username, postId, GetAuthUsername()!))
+ if (!this.IsAdministrator() && !await _service.HasPostModifyPermission(username, id, this.GetUserId()))
{
- return StatusCode(StatusCodes.Status403Forbidden,
- new CommonResponse(ErrorCodes.Http.Timeline.PostOperationDeleteForbid, MessagePostOperationCreateForbid));
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
- await _service.DeletePost(username, postId);
+ await _service.DeletePost(username, id);
+ return Ok(CommonDeleteResponse.Delete());
}
catch (TimelinePostNotExistException)
{
- return BadRequest(new CommonResponse(
- ErrorCodes.Http.Timeline.PostOperationDeleteNotExist,
- MessagePostOperationDeleteNotExist));
+ return Ok(CommonDeleteResponse.NotExist());
}
- return Ok();
}
- [HttpPost("users/{username}/timeline/op/property")]
+ [HttpPatch("users/{username}/timeline")]
[Authorize]
- [SelfOrAdmin]
- [CatchTimelineNotExistException]
- public async Task<ActionResult> TimelineChangeProperty([FromRoute][Username] string username, [FromBody] TimelinePropertyChangeRequest body)
+ public async Task<ActionResult<BaseTimelineInfo>> TimelinePatch([FromRoute][Username] string username, [FromBody] TimelinePatchRequest body)
{
+ if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
await _service.ChangeProperty(username, body);
- return Ok();
+ var timeline = await _service.GetTimeline(username);
+ return Ok(timeline);
}
- [HttpPost("users/{username}/timeline/op/member")]
+ [HttpPut("users/{username}/timeline/members/{member}")]
[Authorize]
- [SelfOrAdmin]
- [CatchTimelineNotExistException]
- public async Task<ActionResult> TimelineChangeMember([FromRoute][Username] string username, [FromBody] TimelineMemberChangeRequest body)
+ public async Task<ActionResult> TimelineMemberPut([FromRoute][Username] string username, [FromRoute][Username] string member)
{
+ if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
try
{
- await _service.ChangeMember(username, body.Add, body.Remove);
+ await _service.ChangeMember(username, new List<string> { member }, null);
return Ok();
}
- catch (TimelineMemberOperationUserException e)
+ catch (UserNotExistException)
{
- if (e.InnerException is UsernameBadFormatException)
- {
- return BadRequest(CommonResponse.InvalidModel(
- string.Format(CultureInfo.CurrentCulture, MessageMemberUsernameBadFormat, e.Index, e.Operation)));
- }
- else if (e.InnerException is UserNotExistException)
- {
- return BadRequest(new CommonResponse(ErrorCodes.Http.Timeline.ChangeMemberUserNotExist,
- string.Format(CultureInfo.CurrentCulture, MessageMemberUserNotExist, e.Index, e.Operation)));
- }
+ return BadRequest(ErrorResponse.TimelineController.MemberPut_NotExist());
+ }
+ }
+
+ [HttpDelete("users/{username}/timeline/members/{member}")]
+ [Authorize]
+ public async Task<ActionResult> TimelineMemberDelete([FromRoute][Username] string username, [FromRoute][Username] string member)
+ {
+ if (!this.IsAdministrator() && !(User.Identity.Name == username))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
- _logger.LogError(e, LogUnknownTimelineMemberOperationUserException);
- throw;
+ try
+ {
+ await _service.ChangeMember(username, null, new List<string> { member });
+ return Ok(CommonDeleteResponse.Delete());
+ }
+ catch (UserNotExistException)
+ {
+ return Ok(CommonDeleteResponse.NotExist());
}
}
}
diff --git a/Timeline/Controllers/Testing/TestingI18nController.cs b/Timeline/Controllers/Testing/TestingI18nController.cs
deleted file mode 100644
index febb56a5..00000000
--- a/Timeline/Controllers/Testing/TestingI18nController.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Localization;
-
-// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
-
-namespace Timeline.Controllers.Testing
-{
- [Route("testing/i18n")]
- [ApiController]
- public class TestingI18nController : Controller
- {
- private readonly IStringLocalizer<TestingI18nController> _stringLocalizer;
-
- public TestingI18nController(IStringLocalizer<TestingI18nController> stringLocalizer)
- {
- _stringLocalizer = stringLocalizer;
- }
-
- [HttpGet("direct")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")]
- public ActionResult<string> Direct()
- {
- return Resources.Controllers.Testing.TestingI18nController.TestString;
- }
-
- [HttpGet("localizer")]
- public ActionResult<string> Localizer()
- {
- return _stringLocalizer["TestString"].Value;
- }
- }
-}
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs
index 01f4778f..1fb0b17a 100644
--- a/Timeline/Controllers/TokenController.cs
+++ b/Timeline/Controllers/TokenController.cs
@@ -1,7 +1,7 @@
+using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
-using Microsoft.IdentityModel.Tokens;
using System;
using System.Globalization;
using System.Threading.Tasks;
@@ -10,46 +10,24 @@ using Timeline.Models.Http;
using Timeline.Services;
using static Timeline.Resources.Controllers.TokenController;
-namespace Timeline
-{
- public static partial class ErrorCodes
- {
- public static partial class Http
- {
- public static class Token // bbb = 001
- {
- public static class Create // cc = 01
- {
- public const int BadCredential = 10010101;
- }
-
- 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 IUserTokenManager _userTokenManager;
private readonly ILogger<TokenController> _logger;
private readonly IClock _clock;
- public TokenController(IUserService userService, ILogger<TokenController> logger, IClock clock)
+ private readonly IMapper _mapper;
+
+ public TokenController(IUserTokenManager userTokenManager, ILogger<TokenController> logger, IClock clock, IMapper mapper)
{
- _userService = userService;
+ _userTokenManager = userTokenManager;
_logger = logger;
_clock = clock;
+ _mapper = mapper;
}
[HttpPost("create")]
@@ -72,7 +50,7 @@ namespace Timeline.Controllers
if (request.Expire != null)
expireTime = _clock.GetCurrentTime().AddDays(request.Expire.Value);
- var result = await _userService.CreateToken(request.Username, request.Password, expireTime);
+ var result = await _userTokenManager.CreateToken(request.Username, request.Password, expireTime);
_logger.LogInformation(Log.Format(LogCreateSuccess,
("Username", request.Username),
@@ -81,22 +59,18 @@ namespace Timeline.Controllers
return Ok(new CreateTokenResponse
{
Token = result.Token,
- User = result.User
+ User = _mapper.Map<UserInfo>(result.User)
});
}
catch (UserNotExistException e)
{
LogFailure(LogUserNotExist, e);
- return BadRequest(new CommonResponse(
- ErrorCodes.Http.Token.Create.BadCredential,
- ErrorBadCredential));
+ return BadRequest(ErrorResponse.TokenController.Create_BadCredential());
}
catch (BadPasswordException e)
{
LogFailure(LogBadPassword, e);
- return BadRequest(new CommonResponse(
- ErrorCodes.Http.Token.Create.BadCredential,
- ErrorBadCredential));
+ return BadRequest(ErrorResponse.TokenController.Create_BadCredential());
}
}
@@ -115,44 +89,34 @@ namespace Timeline.Controllers
try
{
- var result = await _userService.VerifyToken(request.Token);
+ var result = await _userTokenManager.VerifyToken(request.Token);
_logger.LogInformation(Log.Format(LogVerifySuccess,
("Username", result.Username), ("Token", request.Token)));
return Ok(new VerifyTokenResponse
{
- User = result
+ User = _mapper.Map<UserInfo>(result)
});
}
- catch (JwtVerifyException e)
+ catch (UserTokenTimeExpireException e)
{
- if (e.ErrorCode == JwtVerifyException.ErrorCodes.Expired)
- {
- var innerException = e.InnerException as SecurityTokenExpiredException;
- LogFailure(LogVerifyExpire, e, ("Expires", innerException?.Expires),
- ("Current Time", _clock.GetCurrentTime()));
- return BadRequest(new CommonResponse(
- ErrorCodes.Http.Token.Verify.Expired, 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, ErrorVerifyOldVersion));
- }
- else
- {
- LogFailure(LogVerifyBadFormat, e);
- return BadRequest(new CommonResponse(
- ErrorCodes.Http.Token.Verify.BadFormat, ErrorVerifyBadFormat));
- }
+ LogFailure(LogVerifyExpire, e, ("Expire Time", e.ExpireTime), ("Verify Time", e.VerifyTime));
+ return BadRequest(ErrorResponse.TokenController.Verify_TimeExpired());
+ }
+ catch (UserTokenBadVersionException e)
+ {
+ LogFailure(LogVerifyOldVersion, e, ("Token Version", e.TokenVersion), ("Required Version", e.RequiredVersion));
+ return BadRequest(ErrorResponse.TokenController.Verify_OldVersion());
+
+ }
+ catch (UserTokenBadFormatException e)
+ {
+ LogFailure(LogVerifyBadFormat, e);
+ return BadRequest(ErrorResponse.TokenController.Verify_BadFormat());
}
catch (UserNotExistException e)
{
LogFailure(LogVerifyUserNotExist, e);
- return BadRequest(new CommonResponse(
- ErrorCodes.Http.Token.Verify.UserNotExist, ErrorVerifyUserNotExist));
+ return BadRequest(ErrorResponse.TokenController.Verify_UserNotExist());
}
}
}
diff --git a/Timeline/Controllers/UserAvatarController.cs b/Timeline/Controllers/UserAvatarController.cs
index 7625f962..2dd279a8 100644
--- a/Timeline/Controllers/UserAvatarController.cs
+++ b/Timeline/Controllers/UserAvatarController.cs
@@ -14,39 +14,6 @@ using Timeline.Models.Validation;
using Timeline.Services;
using static Timeline.Resources.Controllers.UserAvatarController;
-namespace Timeline
-{
- public static partial class ErrorCodes
- {
- public static partial class Http
- {
- public static class UserAvatar // bbb = 003
- {
- public static class Get // cc = 01
- {
- public const int UserNotExist = 10030101;
- }
-
- 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 class Delete // cc = 03
- {
- public const int UserNotExist = 10030301;
- public const int Forbid = 10030302;
- }
- }
- }
- }
-}
-
namespace Timeline.Controllers
{
[ApiController]
@@ -54,11 +21,13 @@ namespace Timeline.Controllers
{
private readonly ILogger<UserAvatarController> _logger;
+ private readonly IUserService _userService;
private readonly IUserAvatarService _service;
- public UserAvatarController(ILogger<UserAvatarController> logger, IUserAvatarService service)
+ public UserAvatarController(ILogger<UserAvatarController> logger, IUserService userService, IUserAvatarService service)
{
_logger = logger;
+ _userService = userService;
_service = service;
}
@@ -66,41 +35,45 @@ namespace Timeline.Controllers
[ResponseCache(NoStore = false, Location = ResponseCacheLocation.None, Duration = 0)]
public async Task<IActionResult> Get([FromRoute][Username] string username)
{
+ long id;
+ try
+ {
+ id = await _userService.GetUserIdByUsername(username);
+ }
+ catch (UserNotExistException e)
+ {
+ _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
+ return NotFound(ErrorResponse.UserCommon.NotExist());
+ }
+
const string IfNonMatchHeaderKey = "If-None-Match";
- try
+ var eTagValue = $"\"{await _service.GetAvatarETag(id)}\"";
+ var eTag = new EntityTagHeaderValue(eTagValue);
+
+ if (Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var value))
{
- var eTagValue = $"\"{await _service.GetAvatarETag(username)}\"";
- var eTag = new EntityTagHeaderValue(eTagValue);
+ if (!EntityTagHeaderValue.TryParseStrictList(value, out var eTagList))
+ {
+ _logger.LogInformation(Log.Format(LogGetBadIfNoneMatch,
+ ("Username", username), ("If-None-Match", value)));
+ return BadRequest(ErrorResponse.Common.Header.IfNonMatch_BadFormat());
+ }
- if (Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var value))
+ if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
{
- if (!EntityTagHeaderValue.TryParseStrictList(value, out var eTagList))
- {
- _logger.LogInformation(Log.Format(LogGetBadIfNoneMatch,
- ("Username", username), ("If-None-Match", value)));
- return BadRequest(HeaderErrorResponse.BadIfNonMatch());
- }
-
- if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
- {
- Response.Headers.Add("ETag", eTagValue);
- _logger.LogInformation(Log.Format(LogGetReturnNotModify, ("Username", username)));
- return StatusCode(StatusCodes.Status304NotModified);
- }
+ Response.Headers.Add("ETag", eTagValue);
+ _logger.LogInformation(Log.Format(LogGetReturnNotModify, ("Username", username)));
+ return StatusCode(StatusCodes.Status304NotModified);
}
+ }
- var avatarInfo = await _service.GetAvatar(username);
- var avatar = avatarInfo.Avatar;
+ var avatarInfo = await _service.GetAvatar(id);
+ var avatar = avatarInfo.Avatar;
+
+ _logger.LogInformation(Log.Format(LogGetReturnData, ("Username", username)));
+ return File(avatar.Data, avatar.Type, new DateTimeOffset(avatarInfo.LastModified), eTag);
- _logger.LogInformation(Log.Format(LogGetReturnData, ("Username", username)));
- return File(avatar.Data, avatar.Type, new DateTimeOffset(avatarInfo.LastModified), eTag);
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
- return NotFound(new CommonResponse(ErrorCodes.Http.UserAvatar.Get.UserNotExist, ErrorGetUserNotExist));
- }
}
[HttpPut("users/{username}/avatar")]
@@ -111,14 +84,24 @@ namespace Timeline.Controllers
{
var contentLength = Request.ContentLength!.Value;
if (contentLength > 1000 * 1000 * 10)
- return BadRequest(ContentErrorResponse.TooBig("10MB"));
+ return BadRequest(ErrorResponse.Common.Content.TooBig("10MB"));
if (!User.IsAdministrator() && User.Identity.Name != username)
{
_logger.LogInformation(Log.Format(LogPutForbid,
("Operator Username", User.Identity.Name), ("Username To Put Avatar", username)));
- return StatusCode(StatusCodes.Status403Forbidden,
- new CommonResponse(ErrorCodes.Http.UserAvatar.Put.Forbid, ErrorPutForbid));
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
+ long id;
+ try
+ {
+ id = await _userService.GetUserIdByUsername(username);
+ }
+ catch (UserNotExistException e)
+ {
+ _logger.LogInformation(e, Log.Format(LogPutUserNotExist, ("Username", username)));
+ return BadRequest(ErrorResponse.UserCommon.NotExist());
}
try
@@ -127,13 +110,13 @@ namespace Timeline.Controllers
var bytesRead = await Request.Body.ReadAsync(data);
if (bytesRead != contentLength)
- return BadRequest(ContentErrorResponse.UnmatchedLength_Smaller());
+ return BadRequest(ErrorResponse.Common.Content.UnmatchedLength_Smaller());
var extraByte = new byte[1];
if (await Request.Body.ReadAsync(extraByte) != 0)
- return BadRequest(ContentErrorResponse.UnmatchedLength_Bigger());
+ return BadRequest(ErrorResponse.Common.Content.UnmatchedLength_Bigger());
- await _service.SetAvatar(username, new Avatar
+ await _service.SetAvatar(id, new Avatar
{
Data = data,
Type = Request.ContentType
@@ -143,27 +126,17 @@ namespace Timeline.Controllers
("Username", username), ("Mime Type", Request.ContentType)));
return Ok();
}
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogPutUserNotExist, ("Username", username)));
- return BadRequest(new CommonResponse(ErrorCodes.Http.UserAvatar.Put.UserNotExist, ErrorPutUserNotExist));
- }
catch (AvatarFormatException e)
{
- var (code, message) = e.Error switch
+ _logger.LogInformation(e, Log.Format(LogPutUserBadFormat, ("Username", username)));
+ return BadRequest(e.Error switch
{
- AvatarFormatException.ErrorReason.CantDecode =>
- (ErrorCodes.Http.UserAvatar.Put.BadFormat_CantDecode, ErrorPutBadFormatCantDecode),
- AvatarFormatException.ErrorReason.UnmatchedFormat =>
- (ErrorCodes.Http.UserAvatar.Put.BadFormat_UnmatchedFormat, ErrorPutBadFormatUnmatchedFormat),
- AvatarFormatException.ErrorReason.BadSize =>
- (ErrorCodes.Http.UserAvatar.Put.BadFormat_BadSize, ErrorPutBadFormatBadSize),
+ AvatarFormatException.ErrorReason.CantDecode => ErrorResponse.UserAvatar.BadFormat_CantDecode(),
+ AvatarFormatException.ErrorReason.UnmatchedFormat => ErrorResponse.UserAvatar.BadFormat_UnmatchedFormat(),
+ AvatarFormatException.ErrorReason.BadSize => ErrorResponse.UserAvatar.BadFormat_BadSize(),
_ =>
throw new Exception(ExceptionUnknownAvatarFormatError)
- };
-
- _logger.LogInformation(e, Log.Format(LogPutUserBadFormat, ("Username", username)));
- return BadRequest(new CommonResponse(code, message));
+ });
}
}
@@ -173,24 +146,24 @@ namespace Timeline.Controllers
{
if (!User.IsAdministrator() && User.Identity.Name != username)
{
- _logger.LogInformation(Log.Format(LogPutUserBadFormat,
+ _logger.LogInformation(Log.Format(LogDeleteForbid,
("Operator Username", User.Identity.Name), ("Username To Delete Avatar", username)));
- return StatusCode(StatusCodes.Status403Forbidden,
- new CommonResponse(ErrorCodes.Http.UserAvatar.Delete.Forbid, ErrorDeleteForbid));
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
+ long id;
try
{
- await _service.SetAvatar(username, null);
-
- _logger.LogInformation(Log.Format(LogDeleteSuccess, ("Username", username)));
- return Ok();
+ id = await _userService.GetUserIdByUsername(username);
}
catch (UserNotExistException e)
{
_logger.LogInformation(e, Log.Format(LogDeleteNotExist, ("Username", username)));
- return BadRequest(new CommonResponse(ErrorCodes.Http.UserAvatar.Delete.UserNotExist, ErrorDeleteUserNotExist));
+ return BadRequest(ErrorResponse.UserCommon.NotExist());
}
+
+ await _service.SetAvatar(id, null);
+ return Ok();
}
}
}
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs
index 0d950cd7..a3e8d816 100644
--- a/Timeline/Controllers/UserController.cs
+++ b/Timeline/Controllers/UserController.cs
@@ -1,158 +1,124 @@
+using AutoMapper;
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
-using System.Globalization;
+using System.Linq;
using System.Threading.Tasks;
using Timeline.Auth;
using Timeline.Helpers;
-using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Models.Validation;
using Timeline.Services;
using static Timeline.Resources.Controllers.UserController;
-
-namespace Timeline
-{
- public static partial class ErrorCodes
- {
- public static partial class Http
- {
- public static class User // bbb = 002
- {
- public static class Get // cc = 01
- {
- public const int NotExist = 10020101; // dd = 01
- }
-
- public static class Patch // cc = 03
- {
- public const int NotExist = 10020301; // dd = 01
- }
-
- 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 static class ChangePassword // cc = 12
- {
- public const int BadOldPassword = 10021201; // dd = 01
- }
- }
-
- }
- }
- }
-}
+using static Timeline.Resources.Messages;
namespace Timeline.Controllers
{
[ApiController]
public class UserController : Controller
{
-
private readonly ILogger<UserController> _logger;
private readonly IUserService _userService;
+ private readonly IMapper _mapper;
- public UserController(ILogger<UserController> logger, IUserService userService)
+ public UserController(ILogger<UserController> logger, IUserService userService, IMapper mapper)
{
_logger = logger;
_userService = userService;
+ _mapper = mapper;
}
- [HttpGet("users"), AdminAuthorize]
+ private UserInfo ConvertToUserInfo(User user) => _mapper.Map<UserInfo>(user);
+
+ [HttpGet("users")]
public async Task<ActionResult<UserInfo[]>> List()
{
- return Ok(await _userService.ListUsers());
+ var users = await _userService.GetUsers();
+ var result = users.Select(u => ConvertToUserInfo(u)).ToArray();
+ return Ok(result);
}
- [HttpGet("users/{username}"), AdminAuthorize]
+ [HttpGet("users/{username}")]
public async Task<ActionResult<UserInfo>> Get([FromRoute][Username] string username)
{
- var user = await _userService.GetUser(username);
- if (user == null)
+ try
{
- _logger.LogInformation(Log.Format(LogGetUserNotExist, ("Username", username)));
- return NotFound(new CommonResponse(ErrorCodes.Http.User.Get.NotExist, ErrorGetUserNotExist));
+ var user = await _userService.GetUserByUsername(username);
+ return Ok(ConvertToUserInfo(user));
}
- return Ok(user);
- }
-
- [HttpPut("users/{username}"), AdminAuthorize]
- public async Task<ActionResult<CommonPutResponse>> Put([FromBody] UserPutRequest request, [FromRoute][Username] string username)
- {
- var result = await _userService.PutUser(username, request.Password, request.Administrator!.Value);
- switch (result)
+ catch (UserNotExistException e)
{
- case PutResult.Create:
- _logger.LogInformation(Log.Format(LogPutCreate, ("Username", username)));
- return CreatedAtAction("Get", new { username }, CommonPutResponse.Create());
- case PutResult.Modify:
- _logger.LogInformation(Log.Format(LogPutModify, ("Username", username)));
- return Ok(CommonPutResponse.Modify());
- default:
- throw new InvalidBranchException();
+ _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
+ return NotFound(ErrorResponse.UserCommon.NotExist());
}
}
- [HttpPatch("users/{username}"), AdminAuthorize]
- public async Task<ActionResult> Patch([FromBody] UserPatchRequest request, [FromRoute][Username] string username)
+ [HttpPatch("users/{username}"), Authorize]
+ public async Task<ActionResult<UserInfo>> Patch([FromBody] UserPatchRequest body, [FromRoute][Username] string username)
{
- try
+ if (this.IsAdministrator())
{
- await _userService.PatchUser(username, request.Password, request.Administrator);
- return Ok();
+ try
+ {
+ var user = await _userService.ModifyUser(username, _mapper.Map<User>(body));
+ return Ok(ConvertToUserInfo(user));
+ }
+ catch (UserNotExistException e)
+ {
+ _logger.LogInformation(e, Log.Format(LogPatchUserNotExist, ("Username", username)));
+ return NotFound(ErrorResponse.UserCommon.NotExist());
+ }
+ catch (ConflictException)
+ {
+ return BadRequest(ErrorResponse.UserController.UsernameConflict());
+ }
}
- catch (UserNotExistException e)
+ else
{
- _logger.LogInformation(e, Log.Format(LogPatchUserNotExist, ("Username", username)));
- return NotFound(new CommonResponse(ErrorCodes.Http.User.Patch.NotExist, ErrorPatchUserNotExist));
+ if (User.Identity.Name != username)
+ return StatusCode(StatusCodes.Status403Forbidden,
+ ErrorResponse.Common.CustomMessage_Forbid(Common_Forbid_NotSelf));
+
+ if (body.Username != null)
+ return StatusCode(StatusCodes.Status403Forbidden,
+ ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Username));
+
+ if (body.Password != null)
+ return StatusCode(StatusCodes.Status403Forbidden,
+ ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Password));
+
+ if (body.Administrator != null)
+ return StatusCode(StatusCodes.Status403Forbidden,
+ ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Administrator));
+
+ var user = await _userService.ModifyUser(this.GetUserId(), _mapper.Map<User>(body));
+ return Ok(ConvertToUserInfo(user));
}
}
[HttpDelete("users/{username}"), AdminAuthorize]
public async Task<ActionResult<CommonDeleteResponse>> Delete([FromRoute][Username] string username)
{
- try
- {
- await _userService.DeleteUser(username);
- _logger.LogInformation(Log.Format(LogDeleteDelete, ("Username", username)));
+ var delete = await _userService.DeleteUser(username);
+ if (delete)
return Ok(CommonDeleteResponse.Delete());
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogDeleteNotExist, ("Username", username)));
+ else
return Ok(CommonDeleteResponse.NotExist());
- }
}
- [HttpPost("userop/changeusername"), AdminAuthorize]
- public async Task<ActionResult> ChangeUsername([FromBody] ChangeUsernameRequest request)
+ [HttpPost("userop/createuser"), AdminAuthorize]
+ public async Task<ActionResult<UserInfo>> CreateUser([FromBody] CreateUserRequest body)
{
try
{
- await _userService.ChangeUsername(request.OldUsername, request.NewUsername);
- _logger.LogInformation(Log.Format(LogChangeUsernameSuccess,
- ("Old Username", request.OldUsername), ("New Username", request.NewUsername)));
- return Ok();
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogChangeUsernameNotExist,
- ("Old Username", request.OldUsername), ("New Username", request.NewUsername)));
- return BadRequest(new CommonResponse(ErrorCodes.Http.User.Op.ChangeUsername.NotExist,
- string.Format(CultureInfo.CurrentCulture, ErrorChangeUsernameNotExist, request.OldUsername)));
+ var user = await _userService.CreateUser(_mapper.Map<User>(body));
+ return Ok(ConvertToUserInfo(user));
}
- catch (UsernameConfictException e)
+ catch (ConflictException)
{
- _logger.LogInformation(e, Log.Format(LogChangeUsernameAlreadyExist,
- ("Old Username", request.OldUsername), ("New Username", request.NewUsername)));
- return BadRequest(new CommonResponse(ErrorCodes.Http.User.Op.ChangeUsername.AlreadyExist, ErrorChangeUsernameAlreadyExist));
+ return BadRequest(ErrorResponse.UserController.UsernameConflict());
}
- // there is no need to catch bad format exception because it is already checked in model validation.
}
[HttpPost("userop/changepassword"), Authorize]
@@ -160,18 +126,16 @@ namespace Timeline.Controllers
{
try
{
- await _userService.ChangePassword(User.Identity.Name!, request.OldPassword, request.NewPassword);
- _logger.LogInformation(Log.Format(LogChangePasswordSuccess, ("Username", User.Identity.Name)));
+ await _userService.ChangePassword(this.GetUserId(), request.OldPassword, request.NewPassword);
return Ok();
}
catch (BadPasswordException e)
{
_logger.LogInformation(e, Log.Format(LogChangePasswordBadPassword,
("Username", User.Identity.Name), ("Old Password", request.OldPassword)));
- return BadRequest(new CommonResponse(ErrorCodes.Http.User.Op.ChangePassword.BadOldPassword,
- ErrorChangePasswordBadPassword));
+ return BadRequest(ErrorResponse.UserController.ChangePassword_BadOldPassword());
}
- // User can't be non-existent or the token is bad.
+ // 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 9de9899e..00000000
--- a/Timeline/Controllers/UserDetailController.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using System.Threading.Tasks;
-using Timeline.Filters;
-using Timeline.Models.Validation;
-using Timeline.Services;
-using System.ComponentModel.DataAnnotations;
-using Microsoft.AspNetCore.Authorization;
-
-namespace Timeline.Controllers
-{
- [ApiController]
- public class UserDetailController : Controller
- {
- private readonly IUserDetailService _service;
-
- public UserDetailController(IUserDetailService service)
- {
- _service = service;
- }
-
- [HttpGet("users/{username}/nickname")]
- [CatchUserNotExistException]
- public async Task<ActionResult<string>> GetNickname([FromRoute][Username] string username)
- {
- return Ok(await _service.GetNickname(username));
- }
-
- [HttpPut("users/{username}/nickname")]
- [Authorize]
- [SelfOrAdmin]
- [CatchUserNotExistException]
- public async Task<ActionResult> PutNickname([FromRoute][Username] string username,
- [FromBody][StringLength(10, MinimumLength = 1)] string body)
- {
- await _service.SetNickname(username, body);
- return Ok();
- }
-
- [HttpDelete("users/{username}/nickname")]
- [Authorize]
- [SelfOrAdmin]
- [CatchUserNotExistException]
- public async Task<ActionResult> DeleteNickname([FromRoute][Username] string username)
- {
- await _service.SetNickname(username, null);
- return Ok();
- }
- }
-}