Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36 from featbit/feat/login-by-password
✨ Login By Password Api
- Loading branch information
Showing
47 changed files
with
1,139 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
namespace Api.Controllers; | ||
|
||
[ApiController] | ||
[Route("api/v{version:apiVersion}/[controller]")] | ||
public class ApiControllerBase : ControllerBase | ||
{ | ||
private ISender? _mediator; | ||
|
||
protected ISender Mediator | ||
{ | ||
get { return _mediator ??= HttpContext.RequestServices.GetRequiredService<ISender>(); } | ||
} | ||
|
||
protected static ApiResponse<TData> Ok<TData>(TData data) => ApiResponse<TData>.Ok(data); | ||
|
||
protected static ApiResponse<TData> Error<TData>(string errorCode) => ApiResponse<TData>.Error(errorCode); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
namespace Api.Controllers; | ||
|
||
public record ApiResponse<TData> | ||
{ | ||
public bool Success { get; set; } | ||
|
||
public IEnumerable<string> Errors { get; set; } = Array.Empty<string>(); | ||
|
||
public TData? Data { get; set; } | ||
|
||
public static ApiResponse<TData> Ok(TData? data) | ||
{ | ||
return new ApiResponse<TData> | ||
{ | ||
Success = true, | ||
Errors = Array.Empty<string>(), | ||
Data = data | ||
}; | ||
} | ||
|
||
public static ApiResponse<TData> Error(IEnumerable<string> errors) | ||
{ | ||
return new ApiResponse<TData> | ||
{ | ||
Success = false, | ||
Errors = errors, | ||
Data = default | ||
}; | ||
} | ||
|
||
public static ApiResponse<TData> Error(string error) => Error(new[] { error }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
namespace Api.Controllers; | ||
|
||
/// <summary> | ||
/// this controller is intended for testing mvc basic setups (api versioning, consistent api response...) | ||
/// </summary> | ||
[ApiVersion(1.0)] | ||
[ApiVersion(2.0)] | ||
public class BasicController : ApiControllerBase | ||
{ | ||
[HttpGet("string"), MapToApiVersion(1.0)] | ||
public ApiResponse<string> GetStringV1() | ||
{ | ||
return Ok("v1"); | ||
} | ||
|
||
[HttpGet("string"), MapToApiVersion(2.0)] | ||
public ApiResponse<string> GetStringV2() | ||
{ | ||
return Ok("v2"); | ||
} | ||
|
||
[HttpGet("exception")] | ||
public ApiResponse<string> ThrowException() | ||
{ | ||
throw new Exception("exception message"); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
modules/back-end/src/Api/Controllers/ConfigSwaggerOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
using Asp.Versioning.ApiExplorer; | ||
using Microsoft.Extensions.Options; | ||
using Microsoft.OpenApi.Models; | ||
using Swashbuckle.AspNetCore.SwaggerGen; | ||
|
||
namespace Api.Controllers; | ||
|
||
// taken from https://github.com/dotnet/aspnet-api-versioning/blob/main/examples/AspNetCore/WebApi/OpenApiExample/ConfigureSwaggerOptions.cs | ||
|
||
/// <summary> | ||
/// Configures the Swagger generation options. | ||
/// </summary> | ||
/// <remarks>This allows API versioning to define a Swagger document per API version after the | ||
/// <see cref="IApiVersionDescriptionProvider"/> service has been resolved from the service container.</remarks> | ||
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions> | ||
{ | ||
private readonly IApiVersionDescriptionProvider _provider; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class. | ||
/// </summary> | ||
/// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param> | ||
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => _provider = provider; | ||
|
||
/// <inheritdoc /> | ||
public void Configure(SwaggerGenOptions options) | ||
{ | ||
// add a swagger document for each discovered API version | ||
// note: you might choose to skip or document deprecated API versions differently | ||
foreach (var description in _provider.ApiVersionDescriptions) | ||
{ | ||
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); | ||
} | ||
|
||
OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) | ||
{ | ||
var info = new OpenApiInfo() | ||
{ | ||
Title = "FeatBit Backend Api", | ||
Version = description.ApiVersion.ToString(), | ||
License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } | ||
}; | ||
|
||
if (description.IsDeprecated) | ||
{ | ||
info.Description += "<span style=\"color:red\"> This API version has been deprecated.</span>"; | ||
} | ||
|
||
return info; | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
modules/back-end/src/Api/Controllers/IdentityController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using Application.Identity; | ||
|
||
namespace Api.Controllers; | ||
|
||
public class IdentityController : ApiControllerBase | ||
{ | ||
[HttpPost] | ||
[AllowAnonymous] | ||
[Route("login-by-password")] | ||
public async Task<ApiResponse<LoginToken>> LoginByPasswordAsync(LoginByPassword request) | ||
{ | ||
var loginResult = await Mediator.Send(request); | ||
|
||
return loginResult.Success | ||
? Ok(new LoginToken(loginResult.Token)) | ||
: Error<LoginToken>(loginResult.ErrorCode); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
modules/back-end/src/Api/Middlewares/ApiExceptionMiddlewareExtension.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using Api.Controllers; | ||
using Application.Bases; | ||
using Microsoft.AspNetCore.Diagnostics; | ||
|
||
namespace Api.Middlewares; | ||
|
||
public static class ApiExceptionMiddlewareExtension | ||
{ | ||
public static IApplicationBuilder UseApiExceptionHandler(this IApplicationBuilder builder) | ||
{ | ||
return builder.UseExceptionHandler(app => | ||
{ | ||
app.Run(async context => await HandleExceptionAsync(context)); | ||
}); | ||
} | ||
|
||
private static async Task HandleExceptionAsync(HttpContext context) | ||
{ | ||
var exceptionFeature = context.Features.Get<IExceptionHandlerFeature>(); | ||
if (exceptionFeature == null) | ||
{ | ||
return; | ||
} | ||
|
||
var httpResponse = context.Response; | ||
var ex = exceptionFeature.Error; | ||
|
||
// validation exception | ||
if (ex is ValidationException validationException) | ||
{ | ||
httpResponse.StatusCode = StatusCodes.Status400BadRequest; | ||
|
||
var errors = validationException.Errors.Select(x => x.ErrorCode); | ||
var validationError = ApiResponse<object>.Error(errors); | ||
await httpResponse.WriteAsJsonAsync(validationError); | ||
|
||
return; | ||
} | ||
|
||
// other exception | ||
httpResponse.StatusCode = StatusCodes.Status500InternalServerError; | ||
var error = ApiResponse<object>.Error(ErrorCodes.InternalServerError); | ||
await httpResponse.WriteAsJsonAsync(error); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
global using Asp.Versioning; | ||
global using Microsoft.AspNetCore.Mvc; | ||
global using Microsoft.AspNetCore.Authorization; | ||
global using FluentValidation; | ||
global using MediatR; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
modules/back-end/src/Application/Bases/Behaviours/ValidationBehaviour.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
namespace Application.Bases.Behaviours; | ||
|
||
public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> | ||
where TRequest : IRequest<TResponse> | ||
{ | ||
private readonly IEnumerable<IValidator<TRequest>> _validators; | ||
|
||
public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators) | ||
{ | ||
_validators = validators; | ||
} | ||
|
||
public async Task<TResponse> Handle( | ||
TRequest request, | ||
CancellationToken cancellationToken, | ||
RequestHandlerDelegate<TResponse> next) | ||
{ | ||
if (_validators.Any()) | ||
{ | ||
var context = new ValidationContext<TRequest>(request); | ||
|
||
var validationResults = _validators.Select(v => v.Validate(context)); | ||
|
||
var failures = validationResults | ||
.Where(r => r.Errors.Any()) | ||
.SelectMany(r => r.Errors) | ||
.ToList(); | ||
|
||
if (failures.Any()) | ||
{ | ||
throw new ValidationException(failures); | ||
} | ||
} | ||
|
||
return await next(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
namespace Application.Bases; | ||
|
||
public static class ErrorCodes | ||
{ | ||
// general | ||
public const string InternalServerError = nameof(InternalServerError); | ||
|
||
// identity error codes | ||
public const string IdentityIsRequired = nameof(IdentityIsRequired); | ||
public const string PasswordIsRequired = nameof(PasswordIsRequired); | ||
public const string IdentityNotExist = nameof(IdentityNotExist); | ||
public const string IdentityPasswordMismatch = nameof(IdentityPasswordMismatch); | ||
} |
Oops, something went wrong.