From c5199fd23ffffebbb4580fc737a55c9083c00a48 Mon Sep 17 00:00:00 2001 From: namdh861 Date: Thu, 13 Aug 2026 23:12:57 +0700 Subject: [PATCH] Convert Auth module to MediatR command Adds the MediatR package and wires AddMediatR in DI. Replaces IAuthService/AuthService with LoginCommand/LoginHandler, the pattern the remaining modules will follow. --- mws.api/Controllers/AuthController.cs | 7 +++-- mws.application/Auth/AuthService.cs | 35 ----------------------- mws.application/Auth/Commands/Login.cs | 33 +++++++++++++++++++++ mws.application/mws.application.csproj | 1 + mws.infrastructure/DependencyInjection.cs | 2 +- 5 files changed, 39 insertions(+), 39 deletions(-) delete mode 100644 mws.application/Auth/AuthService.cs create mode 100644 mws.application/Auth/Commands/Login.cs diff --git a/mws.api/Controllers/AuthController.cs b/mws.api/Controllers/AuthController.cs index 79c62e9..8ee28c2 100644 --- a/mws.api/Controllers/AuthController.cs +++ b/mws.api/Controllers/AuthController.cs @@ -1,3 +1,4 @@ +using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Mws.Application.Auth; @@ -7,12 +8,12 @@ namespace Mws.Api.Controllers; [ApiController] [Route("api/auth")] [AllowAnonymous] -public class AuthController(IAuthService authService) : ControllerBase +public class AuthController(ISender sender) : ControllerBase { [HttpPost("login")] public async Task> Login([FromBody] LoginRequest request, CancellationToken ct) { - var response = await authService.LoginAsync(request, ct); + var response = await sender.Send(new LoginCommand(request), ct); return Ok(response); } -} \ No newline at end of file +} diff --git a/mws.application/Auth/AuthService.cs b/mws.application/Auth/AuthService.cs deleted file mode 100644 index 1ca89e1..0000000 --- a/mws.application/Auth/AuthService.cs +++ /dev/null @@ -1,35 +0,0 @@ -using AutoMapper; -using Mws.Application.Common; - -namespace Mws.Application.Auth; - -public interface IAuthService -{ - Task LoginAsync(LoginRequest request, CancellationToken cancellationToken = default); -} - -public class AuthService(IUnitOfWork uow, IPasswordHasher passwordHasher, ITokenService tokenService, IMapper mapper) - : IAuthService -{ - public async Task LoginAsync(LoginRequest request, CancellationToken cancellationToken = default) - { - var username = request.Username.Trim(); - var user = await uow.Users.GetByUsernameWithRoleAsync(username, cancellationToken); - - if (user is null || !passwordHasher.Verify(request.Password, user.PasswordHash)) - { - throw new UnauthorizedException("Invalid username or password"); - } - - if (!user.IsActive) - { - throw new ForbiddenException("Account disabled"); - } - - return new LoginResponse - { - Token = tokenService.CreateToken(user.Id, user.Username), - User = mapper.Map(user), - }; - } -} \ No newline at end of file diff --git a/mws.application/Auth/Commands/Login.cs b/mws.application/Auth/Commands/Login.cs new file mode 100644 index 0000000..8de57ae --- /dev/null +++ b/mws.application/Auth/Commands/Login.cs @@ -0,0 +1,33 @@ +using AutoMapper; +using MediatR; +using Mws.Application.Common; + +namespace Mws.Application.Auth; + +public record LoginCommand(LoginRequest Request) : IRequest; + +public class LoginHandler(IUnitOfWork uow, IPasswordHasher passwordHasher, ITokenService tokenService, IMapper mapper) + : IRequestHandler +{ + public async Task Handle(LoginCommand command, CancellationToken ct) + { + var username = command.Request.Username.Trim(); + var user = await uow.Users.GetByUsernameWithRoleAsync(username, ct); + + if (user is null || !passwordHasher.Verify(command.Request.Password, user.PasswordHash)) + { + throw new UnauthorizedException("Invalid username or password"); + } + + if (!user.IsActive) + { + throw new ForbiddenException("Account disabled"); + } + + return new LoginResponse + { + Token = tokenService.CreateToken(user.Id, user.Username), + User = mapper.Map(user), + }; + } +} diff --git a/mws.application/mws.application.csproj b/mws.application/mws.application.csproj index c339a26..439f4ec 100644 --- a/mws.application/mws.application.csproj +++ b/mws.application/mws.application.csproj @@ -12,6 +12,7 @@ + diff --git a/mws.infrastructure/DependencyInjection.cs b/mws.infrastructure/DependencyInjection.cs index ef4e143..f0232e4 100644 --- a/mws.infrastructure/DependencyInjection.cs +++ b/mws.infrastructure/DependencyInjection.cs @@ -25,6 +25,7 @@ public static class DependencyInjection services.AddScoped(); services.AddAutoMapper(cfg => { }, typeof(MappingProfile).Assembly); + services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(IUnitOfWork).Assembly)); services.AddScoped(); @@ -39,7 +40,6 @@ public static class DependencyInjection services.AddSingleton(jwtOptions); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped();