From def8e8dd78812c019a0d6e8e5a3e2de4e82ae3e4 Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Thu, 17 Oct 2019 20:46:57 +0800 Subject: ... --- Timeline.Tests/Timeline.Tests.csproj | 1 + 1 file changed, 1 insertion(+) (limited to 'Timeline.Tests/Timeline.Tests.csproj') diff --git a/Timeline.Tests/Timeline.Tests.csproj b/Timeline.Tests/Timeline.Tests.csproj index 1852da5f..36bc03bc 100644 --- a/Timeline.Tests/Timeline.Tests.csproj +++ b/Timeline.Tests/Timeline.Tests.csproj @@ -14,6 +14,7 @@ + all -- cgit v1.2.3 From b00cfa30a4f6a1c6d896d46da7dd063abf632cd3 Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Sat, 19 Oct 2019 21:37:15 +0800 Subject: ... --- Timeline.Tests/Controllers/TokenControllerTest.cs | 21 ++++++++++ Timeline.Tests/Timeline.Tests.csproj | 49 ++++++++++++----------- Timeline/Controllers/TokenController.cs | 6 +-- Timeline/Entities/DatabaseContext.cs | 10 ++--- Timeline/Entities/UserAvatar.cs | 6 +-- Timeline/Entities/UserDetail.cs | 10 ++--- Timeline/Helpers/InvalidModelResponseFactory.cs | 1 + Timeline/Helpers/Log.cs | 2 +- Timeline/Models/Http/Common.cs | 4 +- Timeline/Models/Http/Token.cs | 12 +++--- Timeline/Models/Http/User.cs | 12 +++--- Timeline/Models/UserDetail.cs | 12 +++--- Timeline/Services/JwtService.cs | 8 +++- Timeline/Startup.cs | 2 + Timeline/Timeline.csproj | 7 +++- 15 files changed, 99 insertions(+), 63 deletions(-) (limited to 'Timeline.Tests/Timeline.Tests.csproj') diff --git a/Timeline.Tests/Controllers/TokenControllerTest.cs b/Timeline.Tests/Controllers/TokenControllerTest.cs index 60ba75dc..8b1cf071 100644 --- a/Timeline.Tests/Controllers/TokenControllerTest.cs +++ b/Timeline.Tests/Controllers/TokenControllerTest.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging.Abstractions; using Moq; using System; +using System.Collections.Generic; using System.Threading.Tasks; using Timeline.Controllers; using Timeline.Models.Http; @@ -90,6 +91,26 @@ namespace Timeline.Tests.Controllers .Which.User.Should().BeEquivalentTo(MockUser.User.Info); } + public static IEnumerable Verify_BadRequest_Data() + { + yield return new object[] { new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.Expired), Verify.Expired }; + yield return new object[] { new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.IdClaimBadFormat), Verify.BadFormat }; + yield return new object[] { new BadTokenVersionException(), Verify.OldVersion }; + yield return new object[] { new UserNotExistException(), Verify.UserNotExist }; + } + + [Theory] + [MemberData(nameof(Verify_BadRequest_Data))] + public async Task Verify_BadRequest(Exception e, int code) + { + const string token = "aaaaaaaaaaaaaa"; + _mockUserService.Setup(s => s.VerifyToken(token)).ThrowsAsync(e); + var action = await _controller.Verify(new VerifyTokenRequest { Token = token }); + action.Should().BeAssignableTo() + .Which.Value.Should().BeAssignableTo() + .Which.Code.Should().Be(code); + } + // TODO! Verify unit tests } } diff --git a/Timeline.Tests/Timeline.Tests.csproj b/Timeline.Tests/Timeline.Tests.csproj index 36bc03bc..3f88f174 100644 --- a/Timeline.Tests/Timeline.Tests.csproj +++ b/Timeline.Tests/Timeline.Tests.csproj @@ -1,28 +1,31 @@  - - netcoreapp3.0 - + + netcoreapp3.0 - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers - - + 8.0 + enable + - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs index 2e661695..ce5786ca 100644 --- a/Timeline/Controllers/TokenController.cs +++ b/Timeline/Controllers/TokenController.cs @@ -54,7 +54,7 @@ namespace Timeline.Controllers [AllowAnonymous] public async Task Create([FromBody] CreateTokenRequest request) { - void LogFailure(string reason, Exception e = null) + void LogFailure(string reason, Exception? e = null) { _logger.LogInformation(e, Log.Format("Attemp to login failed.", ("Reason", reason), @@ -100,7 +100,7 @@ namespace Timeline.Controllers [AllowAnonymous] public async Task Verify([FromBody] VerifyTokenRequest request) { - void LogFailure(string reason, Exception e = null, params (string, object)[] otherProperties) + void LogFailure(string reason, Exception? e = null, params (string, object?)[] otherProperties) { var properties = new (string, object)[2 + otherProperties.Length]; properties[0] = ("Reason", reason); @@ -125,7 +125,7 @@ namespace Timeline.Controllers { const string message = "Token is expired."; var innerException = e.InnerException as SecurityTokenExpiredException; - LogFailure(message, e, ("Expires", innerException.Expires), ("Current Time", _clock.GetCurrentTime())); + LogFailure(message, e, ("Expires", innerException?.Expires), ("Current Time", _clock.GetCurrentTime())); return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Verify.Expired, message)); } else diff --git a/Timeline/Entities/DatabaseContext.cs b/Timeline/Entities/DatabaseContext.cs index d9815660..550db216 100644 --- a/Timeline/Entities/DatabaseContext.cs +++ b/Timeline/Entities/DatabaseContext.cs @@ -17,20 +17,20 @@ namespace Timeline.Entities public long Id { get; set; } [Column("name"), MaxLength(26), Required] - public string Name { get; set; } + public string Name { get; set; } = default!; [Column("password"), Required] - public string EncryptedPassword { get; set; } + public string EncryptedPassword { get; set; } = default!; [Column("roles"), Required] - public string RoleString { get; set; } + public string RoleString { get; set; } = default!; [Column("version"), Required] public long Version { get; set; } - public UserAvatar Avatar { get; set; } + public UserAvatar? Avatar { get; set; } - public UserDetailEntity Detail { get; set; } + public UserDetailEntity? Detail { get; set; } } public class DatabaseContext : DbContext diff --git a/Timeline/Entities/UserAvatar.cs b/Timeline/Entities/UserAvatar.cs index d549aea5..d47bb28b 100644 --- a/Timeline/Entities/UserAvatar.cs +++ b/Timeline/Entities/UserAvatar.cs @@ -11,13 +11,13 @@ namespace Timeline.Entities public long Id { get; set; } [Column("data")] - public byte[] Data { get; set; } + public byte[]? Data { get; set; } [Column("type")] - public string Type { get; set; } + public string? Type { get; set; } [Column("etag"), MaxLength(30)] - public string ETag { get; set; } + public string? ETag { get; set; } [Column("last_modified"), Required] public DateTime LastModified { get; set; } diff --git a/Timeline/Entities/UserDetail.cs b/Timeline/Entities/UserDetail.cs index bc14dbe6..e02d15c4 100644 --- a/Timeline/Entities/UserDetail.cs +++ b/Timeline/Entities/UserDetail.cs @@ -10,19 +10,19 @@ namespace Timeline.Entities public long Id { get; set; } [Column("nickname"), MaxLength(15)] - public string Nickname { get; set; } + public string? Nickname { get; set; } [Column("qq"), MaxLength(15)] - public string QQ { get; set; } + public string? QQ { get; set; } [Column("email"), MaxLength(50)] - public string Email { get; set; } + public string? Email { get; set; } [Column("phone_number"), MaxLength(15)] - public string PhoneNumber { get; set; } + public string? PhoneNumber { get; set; } [Column("description")] - public string Description { get; set; } + public string? Description { get; set; } public long UserId { get; set; } } diff --git a/Timeline/Helpers/InvalidModelResponseFactory.cs b/Timeline/Helpers/InvalidModelResponseFactory.cs index c792e845..643c99ac 100644 --- a/Timeline/Helpers/InvalidModelResponseFactory.cs +++ b/Timeline/Helpers/InvalidModelResponseFactory.cs @@ -6,6 +6,7 @@ namespace Timeline.Helpers { public static class InvalidModelResponseFactory { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods")] public static IActionResult Factory(ActionContext context) { var modelState = context.ModelState; diff --git a/Timeline/Helpers/Log.cs b/Timeline/Helpers/Log.cs index 64391cd1..8deebf1d 100644 --- a/Timeline/Helpers/Log.cs +++ b/Timeline/Helpers/Log.cs @@ -25,7 +25,7 @@ namespace Timeline.Helpers public static class Log { - public static string Format(string summary, params (string, object)[] properties) + public static string Format(string summary, params (string, object?)[] properties) { var builder = new StringBuilder(); builder.Append(summary); diff --git a/Timeline/Models/Http/Common.cs b/Timeline/Models/Http/Common.cs index 83e6a072..6f6dbc1e 100644 --- a/Timeline/Models/Http/Common.cs +++ b/Timeline/Models/Http/Common.cs @@ -38,8 +38,8 @@ namespace Timeline.Models.Http Message = message; } - public int? Code { get; set; } - public string Message { get; set; } + public int Code { get; set; } + public string? Message { get; set; } } public class CommonDataResponse : CommonResponse diff --git a/Timeline/Models/Http/Token.cs b/Timeline/Models/Http/Token.cs index 615b6d8a..ea8b59ed 100644 --- a/Timeline/Models/Http/Token.cs +++ b/Timeline/Models/Http/Token.cs @@ -5,9 +5,9 @@ namespace Timeline.Models.Http public class CreateTokenRequest { [Required] - public string Username { get; set; } + public string Username { get; set; } = default!; [Required] - public string Password { get; set; } + public string Password { get; set; } = default!; // in days, optional [Range(1, 365)] public int? Expire { get; set; } @@ -15,18 +15,18 @@ namespace Timeline.Models.Http public class CreateTokenResponse { - public string Token { get; set; } - public UserInfo User { get; set; } + public string Token { get; set; } = default!; + public UserInfo User { get; set; } = default!; } public class VerifyTokenRequest { [Required] - public string Token { get; set; } + public string Token { get; set; } = default!; } public class VerifyTokenResponse { - public UserInfo User { get; set; } + public UserInfo User { get; set; } = default!; } } diff --git a/Timeline/Models/Http/User.cs b/Timeline/Models/Http/User.cs index 4308a19c..98406fec 100644 --- a/Timeline/Models/Http/User.cs +++ b/Timeline/Models/Http/User.cs @@ -6,31 +6,31 @@ namespace Timeline.Models.Http public class UserPutRequest { [Required] - public string Password { get; set; } + public string Password { get; set; } = default!; [Required] public bool? Administrator { get; set; } } public class UserPatchRequest { - public string Password { get; set; } + public string? Password { get; set; } public bool? Administrator { get; set; } } public class ChangeUsernameRequest { [Required] - public string OldUsername { get; set; } + public string OldUsername { get; set; } = default!; [Required, ValidateWith(typeof(UsernameValidator))] - public string NewUsername { get; set; } + public string NewUsername { get; set; } = default!; } public class ChangePasswordRequest { [Required] - public string OldPassword { get; set; } + public string OldPassword { get; set; } = default!; [Required] - public string NewPassword { get; set; } + public string NewPassword { get; set; } = default!; } } diff --git a/Timeline/Models/UserDetail.cs b/Timeline/Models/UserDetail.cs index 1a6c0c6a..302e3bb1 100644 --- a/Timeline/Models/UserDetail.cs +++ b/Timeline/Models/UserDetail.cs @@ -8,21 +8,21 @@ namespace Timeline.Models public class UserDetail { [MaxLength(10)] - public string Nickname { get; set; } + public string? Nickname { get; set; } [ValidateWith(typeof(UserDetailValidators.QQValidator))] [JsonProperty(PropertyName = "qq")] - public string QQ { get; set; } + public string? QQ { get; set; } [ValidateWith(typeof(UserDetailValidators.EMailValidator))] - public string Email { get; set; } + public string? Email { get; set; } [ValidateWith(typeof(UserDetailValidators.PhoneNumberValidator))] - public string PhoneNumber { get; set; } + public string? PhoneNumber { get; set; } - public string Description { get; set; } + public string? Description { get; set; } - private static string CoerceEmptyToNull(string value) + private static string? CoerceEmptyToNull(string? value) { if (string.IsNullOrEmpty(value)) return null; diff --git a/Timeline/Services/JwtService.cs b/Timeline/Services/JwtService.cs index 350c5e80..90d0c217 100644 --- a/Timeline/Services/JwtService.cs +++ b/Timeline/Services/JwtService.cs @@ -33,6 +33,12 @@ namespace Timeline.Services public const int Expired = -2001; } + private const string message = "Jwt token is bad."; + + public JwtTokenVerifyException() : base(message) { } + public JwtTokenVerifyException(string message) : base(message) { } + public JwtTokenVerifyException(string message, Exception inner) : base(message, inner) { } + public JwtTokenVerifyException(int code) : base(GetErrorMessage(code)) { ErrorCode = code; } public JwtTokenVerifyException(string message, int code) : base(message) { ErrorCode = code; } public JwtTokenVerifyException(Exception inner, int code) : base(GetErrorMessage(code), inner) { ErrorCode = code; } @@ -41,7 +47,7 @@ namespace Timeline.Services System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - public int ErrorCode { get; private set; } + public int ErrorCode { get; set; } private static string GetErrorMessage(int errorCode) { diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs index fc570fdd..ddbe0840 100644 --- a/Timeline/Startup.cs +++ b/Timeline/Startup.cs @@ -13,6 +13,7 @@ using Timeline.Services; namespace Timeline { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")] public class Startup { public Startup(IConfiguration configuration, IWebHostEnvironment environment) @@ -69,6 +70,7 @@ namespace Timeline services.AddMemoryCache(); } + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj index 836dfb47..c634563a 100644 --- a/Timeline/Timeline.csproj +++ b/Timeline/Timeline.csproj @@ -4,6 +4,9 @@ false 1f6fb74d-4277-4bc0-aeea-b1fc5ffb0b43 crupest + + 8.0 + enable @@ -15,8 +18,8 @@ - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive -- cgit v1.2.3 From 9bc08731aad112f100bcbc105f2cca910948651f Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Sun, 20 Oct 2019 16:24:11 +0800 Subject: ... --- Timeline.Tests/Controllers/TokenControllerTest.cs | 13 +- Timeline.Tests/GlobalSuppressions.cs | 9 ++ .../Mock/Services/MockStringLocalizer.cs | 31 +++++ Timeline.Tests/Timeline.Tests.csproj | 6 +- Timeline/Controllers/TokenController.cs | 34 +++-- .../Controllers/TokenController.en.Designer.cs | 72 ++++++++++ .../Resources/Controllers/TokenController.en.resx | 153 +++++++++++++++++++++ Timeline/Startup.cs | 5 + Timeline/Timeline.csproj | 15 ++ 9 files changed, 318 insertions(+), 20 deletions(-) create mode 100644 Timeline.Tests/GlobalSuppressions.cs create mode 100644 Timeline.Tests/Mock/Services/MockStringLocalizer.cs create mode 100644 Timeline/Resources/Controllers/TokenController.en.Designer.cs create mode 100644 Timeline/Resources/Controllers/TokenController.en.resx (limited to 'Timeline.Tests/Timeline.Tests.csproj') diff --git a/Timeline.Tests/Controllers/TokenControllerTest.cs b/Timeline.Tests/Controllers/TokenControllerTest.cs index 8b1cf071..86a241e5 100644 --- a/Timeline.Tests/Controllers/TokenControllerTest.cs +++ b/Timeline.Tests/Controllers/TokenControllerTest.cs @@ -15,7 +15,7 @@ using static Timeline.ErrorCodes.Http.Token; namespace Timeline.Tests.Controllers { - public class TokenControllerTest + public class TokenControllerTest : IDisposable { private readonly Mock _mockUserService = new Mock(); private readonly TestClock _mockClock = new TestClock(); @@ -24,7 +24,14 @@ namespace Timeline.Tests.Controllers public TokenControllerTest() { - _controller = new TokenController(_mockUserService.Object, NullLogger.Instance, _mockClock); + _controller = new TokenController(_mockUserService.Object, + NullLogger.Instance, _mockClock, + new MockStringLocalizer()); + } + + public void Dispose() + { + _controller.Dispose(); } [Theory] @@ -110,7 +117,5 @@ namespace Timeline.Tests.Controllers .Which.Value.Should().BeAssignableTo() .Which.Code.Should().Be(code); } - - // TODO! Verify unit tests } } diff --git a/Timeline.Tests/GlobalSuppressions.cs b/Timeline.Tests/GlobalSuppressions.cs new file mode 100644 index 00000000..6562efbb --- /dev/null +++ b/Timeline.Tests/GlobalSuppressions.cs @@ -0,0 +1,9 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "This is not a UI application.")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Tests name have underscores.")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1063:Implement IDisposable Correctly", Justification = "Test classes do not need to implement it that way.")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA1816:Dispose methods should call SuppressFinalize", Justification = "Test classes do not need to implement it that way.")] diff --git a/Timeline.Tests/Mock/Services/MockStringLocalizer.cs b/Timeline.Tests/Mock/Services/MockStringLocalizer.cs new file mode 100644 index 00000000..7729d56c --- /dev/null +++ b/Timeline.Tests/Mock/Services/MockStringLocalizer.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Localization; +using System.Collections.Generic; +using System.Globalization; + +namespace Timeline.Tests.Mock.Services +{ + public class MockStringLocalizer : IStringLocalizer + { + private const string mockKey = "MOCK_KEY"; + private const string mockString = "THIS IS A MOCK LOCALIZED STRING."; + + public LocalizedString this[string name] => new LocalizedString(name, mockString); + + public LocalizedString this[string name, params object[] arguments] => new LocalizedString(name, mockString); + + public IEnumerable GetAllStrings(bool includeParentCultures) + { + yield return new LocalizedString(mockKey, mockString); + } + + public IStringLocalizer WithCulture(CultureInfo culture) + { + return this; + } + } + + public class MockStringLocalizer : MockStringLocalizer, IStringLocalizer + { + + } +} diff --git a/Timeline.Tests/Timeline.Tests.csproj b/Timeline.Tests/Timeline.Tests.csproj index 3f88f174..a611dfd3 100644 --- a/Timeline.Tests/Timeline.Tests.csproj +++ b/Timeline.Tests/Timeline.Tests.csproj @@ -15,9 +15,13 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + all diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs index ce5786ca..eba69319 100644 --- a/Timeline/Controllers/TokenController.cs +++ b/Timeline/Controllers/TokenController.cs @@ -7,6 +7,8 @@ using System.Threading.Tasks; using Timeline.Models.Http; using Timeline.Services; using Timeline.Helpers; +using Microsoft.Extensions.Localization; +using System.Globalization; namespace Timeline { @@ -42,12 +44,14 @@ namespace Timeline.Controllers private readonly IUserService _userService; private readonly ILogger _logger; private readonly IClock _clock; + private readonly IStringLocalizer _localizer; - public TokenController(IUserService userService, ILogger logger, IClock clock) + public TokenController(IUserService userService, ILogger logger, IClock clock, IStringLocalizer localizer) { _userService = userService; _logger = logger; _clock = clock; + _localizer = localizer; } [HttpPost("create")] @@ -56,7 +60,7 @@ namespace Timeline.Controllers { void LogFailure(string reason, Exception? e = null) { - _logger.LogInformation(e, Log.Format("Attemp to login failed.", + _logger.LogInformation(e, Log.Format(_localizer["LogCreateFailure"], ("Reason", reason), ("Username", request.Username), ("Password", request.Password), @@ -72,9 +76,9 @@ namespace Timeline.Controllers var result = await _userService.CreateToken(request.Username, request.Password, expireTime); - _logger.LogInformation(Log.Format("Attemp to login succeeded.", + _logger.LogInformation(Log.Format(_localizer["LogCreateSuccess"], ("Username", request.Username), - ("Expire At", expireTime?.ToString() ?? "default") + ("Expire At", expireTime?.ToString(CultureInfo.CurrentUICulture.DateTimeFormat) ?? "default") )); return Ok(new CreateTokenResponse { @@ -84,15 +88,15 @@ namespace Timeline.Controllers } catch (UserNotExistException e) { - LogFailure("User does not exist.", e); + LogFailure(_localizer["LogUserNotExist"], e); return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Create.BadCredential, - "Bad username or password.")); + _localizer["ErrorBadCredential"])); } catch (BadPasswordException e) { - LogFailure("Password is wrong.", e); + LogFailure(_localizer["LogBadPassword"], e); return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Create.BadCredential, - "Bad username or password.")); + _localizer["ErrorBadCredential"])); } } @@ -102,17 +106,17 @@ namespace Timeline.Controllers { void LogFailure(string reason, Exception? e = null, params (string, object?)[] otherProperties) { - var properties = new (string, object)[2 + otherProperties.Length]; + 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)); + _logger.LogInformation(e, Log.Format(_localizer["LogVerifyFailure"], properties)); } try { var result = await _userService.VerifyToken(request.Token); - _logger.LogInformation(Log.Format("Token verification succeeded.", + _logger.LogInformation(Log.Format(_localizer["LogVerifySuccess"], ("Username", result.Username), ("Token", request.Token))); return Ok(new VerifyTokenResponse { @@ -123,27 +127,27 @@ namespace Timeline.Controllers { if (e.ErrorCode == JwtTokenVerifyException.ErrorCodes.Expired) { - const string message = "Token is expired."; + string message = _localizer["ErrorVerifyExpire"]; var innerException = e.InnerException as SecurityTokenExpiredException; 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."; + string message = _localizer["ErrorVerifyBadFormat"]; 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."; + string message = _localizer["ErrorVerifyUserNotExist"]; LogFailure(message, e); return BadRequest(new CommonResponse(ErrorCodes.Http.Token.Verify.UserNotExist, message)); } catch (BadTokenVersionException e) { - const string message = "Token has an old version."; + string message = _localizer["ErrorVerifyOldVersion"]; 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/Resources/Controllers/TokenController.en.Designer.cs b/Timeline/Resources/Controllers/TokenController.en.Designer.cs new file mode 100644 index 00000000..64326860 --- /dev/null +++ b/Timeline/Resources/Controllers/TokenController.en.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Timeline.Resources.Controllers { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class TokenController { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TokenController() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.TokenController", typeof(TokenController).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to A user succeeded to create a token.. + /// + public static string LogCreateSuccess { + get { + return ResourceManager.GetString("LogCreateSuccess", resourceCulture); + } + } + } +} diff --git a/Timeline/Resources/Controllers/TokenController.en.resx b/Timeline/Resources/Controllers/TokenController.en.resx new file mode 100644 index 00000000..7309ea6a --- /dev/null +++ b/Timeline/Resources/Controllers/TokenController.en.resx @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Username or password is invalid. + + + The token is of bad format. It might not be created by the server. + + + The token is expired. + + + Token has an old version. User might have update some info. + + + User does not exist. Administrator might have deleted this user. + + + The password is wrong. + + + A user failed to create a token. + + + A user succeeded to create a token. + + + The user does not exist. + + + A token failed to be verified. + + + A token succeeded to be verified. + + \ No newline at end of file diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs index ddbe0840..72c9bf32 100644 --- a/Timeline/Startup.cs +++ b/Timeline/Startup.cs @@ -50,6 +50,11 @@ namespace Timeline ); }); + services.AddLocalization(options => + { + options.ResourcesPath = "Resources"; + }); + services.AddScoped(); services.AddScoped(); services.AddTransient(); diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj index c634563a..01207ae2 100644 --- a/Timeline/Timeline.csproj +++ b/Timeline/Timeline.csproj @@ -26,4 +26,19 @@ + + + + True + True + TokenController.en.resx + + + + + + PublicResXFileCodeGenerator + TokenController.en.Designer.cs + + -- cgit v1.2.3 From 4c1a20746abc3354d67ffd7ef65e8639e70cd63a Mon Sep 17 00:00:00 2001 From: 杨宇千 Date: Sun, 20 Oct 2019 16:47:17 +0800 Subject: ... --- .../IntegratedTests/AuthorizationUnitTest.cs | 9 +++--- Timeline.Tests/Timeline.Tests.csproj | 1 - .../Controllers/Testing/TestingAuthController.cs | 32 ++++++++++++++++++++++ Timeline/Controllers/UserTestController.cs | 32 ---------------------- Timeline/Startup.cs | 16 +++++++++++ 5 files changed, 53 insertions(+), 37 deletions(-) create mode 100644 Timeline/Controllers/Testing/TestingAuthController.cs delete mode 100644 Timeline/Controllers/UserTestController.cs (limited to 'Timeline.Tests/Timeline.Tests.csproj') diff --git a/Timeline.Tests/IntegratedTests/AuthorizationUnitTest.cs b/Timeline.Tests/IntegratedTests/AuthorizationUnitTest.cs index a67bffcf..588e4349 100644 --- a/Timeline.Tests/IntegratedTests/AuthorizationUnitTest.cs +++ b/Timeline.Tests/IntegratedTests/AuthorizationUnitTest.cs @@ -11,10 +11,6 @@ namespace Timeline.Tests.IntegratedTests { public class AuthorizationUnitTest : IClassFixture>, IDisposable { - private const string AuthorizeUrl = "Test/User/Authorize"; - private const string UserUrl = "Test/User/User"; - private const string AdminUrl = "Test/User/Admin"; - private readonly TestApplication _testApp; private readonly WebApplicationFactory _factory; @@ -29,6 +25,11 @@ namespace Timeline.Tests.IntegratedTests _testApp.Dispose(); } + private const string BaseUrl = "testing/auth/"; + private const string AuthorizeUrl = BaseUrl + "Authorize"; + private const string UserUrl = BaseUrl + "User"; + private const string AdminUrl = BaseUrl + "Admin"; + [Fact] public async Task UnauthenticationTest() { diff --git a/Timeline.Tests/Timeline.Tests.csproj b/Timeline.Tests/Timeline.Tests.csproj index a611dfd3..497a00b7 100644 --- a/Timeline.Tests/Timeline.Tests.csproj +++ b/Timeline.Tests/Timeline.Tests.csproj @@ -4,7 +4,6 @@ netcoreapp3.0 8.0 - enable diff --git a/Timeline/Controllers/Testing/TestingAuthController.cs b/Timeline/Controllers/Testing/TestingAuthController.cs new file mode 100644 index 00000000..488a3cff --- /dev/null +++ b/Timeline/Controllers/Testing/TestingAuthController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Timeline.Authenticate; + +namespace Timeline.Controllers.Testing +{ + [Route("testing/auth")] + [ApiController] + public class TestingAuthController : Controller + { + [HttpGet("[action]")] + [Authorize] + public ActionResult Authorize() + { + return Ok(); + } + + [HttpGet("[action]")] + [UserAuthorize] + public new ActionResult User() + { + return Ok(); + } + + [HttpGet("[action]")] + [AdminAuthorize] + public ActionResult Admin() + { + return Ok(); + } + } +} diff --git a/Timeline/Controllers/UserTestController.cs b/Timeline/Controllers/UserTestController.cs deleted file mode 100644 index 2a5f36a1..00000000 --- a/Timeline/Controllers/UserTestController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Timeline.Authenticate; - -namespace Timeline.Controllers -{ - [Route("Test/User")] - [ApiController] - public class UserTestController : Controller - { - [HttpGet("[action]")] - [Authorize] - public ActionResult Authorize() - { - return Ok(); - } - - [HttpGet("[action]")] - [UserAuthorize] - public new ActionResult User() - { - return Ok(); - } - - [HttpGet("[action]")] - [AdminAuthorize] - public ActionResult Admin() - { - return Ok(); - } - } -} diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs index 72c9bf32..5718cf05 100644 --- a/Timeline/Startup.cs +++ b/Timeline/Startup.cs @@ -2,9 +2,12 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Localization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; +using System.Globalization; using Timeline.Authenticate; using Timeline.Configs; using Timeline.Entities; @@ -86,6 +89,19 @@ namespace Timeline app.UseRouting(); + var supportedCultures = new List + { + new CultureInfo("en"), + new CultureInfo("zh") + }; + + app.UseRequestLocalization(new RequestLocalizationOptions + { + DefaultRequestCulture = new RequestCulture("en"), + SupportedCultures = supportedCultures, + SupportedUICultures = supportedCultures + }); + app.UseCors(); app.UseAuthentication(); -- cgit v1.2.3