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.
This commit is contained in:
2026-08-13 23:12:57 +07:00
parent f72aaa2329
commit c5199fd23f
5 changed files with 39 additions and 39 deletions
+4 -3
View File
@@ -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<ActionResult<LoginResponse>> 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);
}
}
}
-35
View File
@@ -1,35 +0,0 @@
using AutoMapper;
using Mws.Application.Common;
namespace Mws.Application.Auth;
public interface IAuthService
{
Task<LoginResponse> LoginAsync(LoginRequest request, CancellationToken cancellationToken = default);
}
public class AuthService(IUnitOfWork uow, IPasswordHasher passwordHasher, ITokenService tokenService, IMapper mapper)
: IAuthService
{
public async Task<LoginResponse> 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<UserDto>(user),
};
}
}
+33
View File
@@ -0,0 +1,33 @@
using AutoMapper;
using MediatR;
using Mws.Application.Common;
namespace Mws.Application.Auth;
public record LoginCommand(LoginRequest Request) : IRequest<LoginResponse>;
public class LoginHandler(IUnitOfWork uow, IPasswordHasher passwordHasher, ITokenService tokenService, IMapper mapper)
: IRequestHandler<LoginCommand, LoginResponse>
{
public async Task<LoginResponse> 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<UserDto>(user),
};
}
}
+1
View File
@@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="16.2.0" />
<PackageReference Include="MediatR" Version="14.2.0" />
</ItemGroup>
</Project>
+1 -1
View File
@@ -25,6 +25,7 @@ public static class DependencyInjection
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddAutoMapper(cfg => { }, typeof(MappingProfile).Assembly);
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(IUnitOfWork).Assembly));
services.AddScoped<IPasswordHasher, BcryptPasswordHasher>();
@@ -39,7 +40,6 @@ public static class DependencyInjection
services.AddSingleton(jwtOptions);
services.AddScoped<ITokenService, JwtTokenService>();
services.AddScoped<IAuthService, AuthService>();
services.AddScoped<IAccountService, AccountService>();
services.AddScoped<IPermissionService, PermissionService>();
services.AddScoped<IRoleService, RoleService>();