I am having a Project on ASP.NET Core-6 Web API using MapsterMapper, Fluent Validation and IMediatR
Interfaces:
public interface IRequestWrapper<T> : IRequest<ServiceResult<T>>
{
}
public interface IRequestHandlerWrapper<in TRequest, TResponse> : IRequestHandler<TRequest, ServiceResult<TResponse>>
where TRequest : IRequestWrapper<TResponse>
{
}
I did the response with ServiceResult as shown below.
ServiceResult:
public class ServiceResult<T> : ServiceResult
{
public T Data { get; set; }
public ServiceResult(T data)
{
Data = data;
}
public ServiceResult(T data, ServiceError error) : base(error)
{
Data = data;
}
public ServiceResult(ServiceError error) : base(error)
{
}
}
public class ServiceResult
{
public bool Successful => this.Error == null;
public ServiceError Error { get; set; }
public ServiceResult(ServiceError error)
{
if (error == null)
{
error = ServiceError.DefaultError;
}
Error = error;
}
public ServiceResult() { }
public static ServiceResult Failed(ServiceError error)
{
return new ServiceResult(error);
}
public static ServiceResult<T> Failed<T>(ServiceError error)
{
return new ServiceResult<T>(error);
}
public static ServiceResult<T> Failed<T>(T data, ServiceError error)
{
return new ServiceResult<T>(data, error);
}
public static ServiceResult<T> Success<T>(T data)
{
return new ServiceResult<T>(data);
}
}
ApplicationUserDto:
public class ApplicationUserDto : IRequest
{
public String Id { get; set; }
public string Email { get; set; }
public string UserName { get; set; }
public string MobileNumber { get; set; }
public bool? IsAdmin { get; set; }
public DateTime LastLogin { get; set; }
public void Register(TypeAdapterConfig config)
{
config.NewConfig<ApplicationUser, ApplicationUserDto>();
}
}
AuthResult:
public class AuthResult
{
public string AccessToken { get; set; }
public string TokenType { get; set; }
public int ExpiresIn { get; set; }
public string RefreshToken { get; set; }
public ApplicationUserDto User { get; set; }
public IList<string> Roles { get; set; }
}
JwtTokenManager:
using MapsterMapper;
public class JwtTokenManager : IJwtTokenManager
{
private readonly JwtSettings _jwtSettings;
private readonly UserManager<ApplicationUser> _userManager;
private readonly TokenValidationParameters _tokenValidationParameters;
private readonly IMapper _mapper;
public JwtTokenManager(
JwtSettings jwtSettings,
UserManager<ApplicationUser> userManager,
TokenValidationParameters tokenValidationParameters,
IMapper mapper
)
{
_jwtSettings = jwtSettings;
_userManager = userManager;
_tokenValidationParameters = tokenValidationParameters;
_mapper = mapper;
}
public async Task<AuthResult> GenerateClaimsTokenAsync(string username, CancellationToken cancellationToken)
{
var user = await _userManager.FindByNameAsync(username);
var roles = await _userManager.GetRolesAsync(user);
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_jwtSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // TODO: encrypt user id for added security
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Sub, username),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddMinutes(5)).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}),
Expires = DateTime.UtcNow.Add(_jwtSettings.Expiration),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var refreshTokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // TODO: encrypt user id for added security
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Iss, _jwtSettings.Issuer),
new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddMinutes(30)).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}),
Expires = DateTime.UtcNow.Add(_jwtSettings.Expiration),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
// Create JWT tokens
var token = tokenHandler.CreateToken(tokenDescriptor);
var refreshtoken = tokenHandler.CreateToken(refreshTokenDescriptor);
return new AuthResult
{
AccessToken = tokenHandler.WriteToken(token),
TokenType = "Bearer",
ExpiresIn = _jwtSettings.Expiration.Seconds,
RefreshToken = tokenHandler.WriteToken(refreshtoken),
User = _mapper.Map<ApplicationUserDto>(user),
Roles = roles
};
}
}
SignInCommand:
public class SignInCommand : IRequestWrapper<AuthResult>
{
public string Username { get; set; }
public string Password { get; set; }
}
the return AuthResult in JwtTokenManager is passed to SignInCommandHandler. Also SignInCommand is the request while AuthResult is the response.
SignInCommandHandler:
public class SignInCommandHandler : IRequestHandlerWrapper<SignInCommand, AuthResult>
{
private readonly ISignInManager _signInManager;
private readonly IJwtTokenManager _jwtTokenManager;
private readonly IIdentityService _identityService;
public SignInCommandHandler(ISignInManager signInManager, IJwtTokenManager jwtTokenManager, IIdentityService identityService)
{
_signInManager = signInManager;
_jwtTokenManager = jwtTokenManager;
_identityService = identityService;
}
public async Task<ServiceResult<AuthResult>> Handle(SignInCommand request, CancellationToken cancellationToken)
{
// validate username & password
var result = await _signInManager.PasswordSignInAsync(request.Username.ToLower().Trim(), request.Password.Trim(), false, false);
// Throw exception if credential validation failed
if (!result.Successful)
{
throw new UnauthorizedException("Invalid username or password.");
}
// Generate JWT token response if validation successful
return ServiceResult.Success(await _jwtTokenManager.GenerateClaimsTokenAsync(request.Username, cancellationToken));
}
}
Now the Controller:
public class AuthController : BaseApiController
{
private readonly IMediator _mediator;
public AuthController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
[Route(ApiRoutes.Auth.SignIn)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<ServiceResult<AuthResult>>> SignIn(SignInRequest request, CancellationToken cancellationToken)
{
return Ok(await _mediator.Send(request, cancellationToken));
}
}
DependencyInjection:
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
// Register OAuth services
services.AddTransient<IJwtTokenManager, JwtTokenManager>();
services.AddScoped<ISignInManager, SignInManager>();
services.AddTransient<IIdentityService, IdentityService>();
// Register Application Health Checks
services.AddHealthChecks()
.AddCheck<ApplicationHealthCheck>(name: "MY API");
// Register Fluent Validation service
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
// Register MediatR Services
services.AddMediatR(Assembly.GetExecutingAssembly());
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>));
return services;
}
private static TypeAdapterConfig GetConfiguredMappingConfig()
{
var config = TypeAdapterConfig.GlobalSettings;
IList<IRegister> registers = config.Scan(Assembly.GetExecutingAssembly());
config.Apply(registers);
return config;
}
}
Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add library project references for DependencyInjection
builder.Services.AddApplication();
When I tried to Run the Application, I got this error:
System.AggregateException
HResult=0x80131500
Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2[Auth.Commands.SignIn.SignInCommand,Models.ServiceResult`1[Models.AuthResult]] Lifetime: Transient ImplementationType: Auth.Commands.SignIn.SignInCommandHandler': Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Oauth.JwtTokenManager'.)
(Error while validating the service descriptor 'ServiceType: Interfaces.IJwtTokenManager Lifetime: Transient ImplementationType: Oauth.JwtTokenManager': Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Oauth.JwtTokenManager'.)
Source=Microsoft.Extensions.DependencyInjection
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at Microsoft.AspNetCore.Builder.WebApplicationBuilder.Build()
at Program.<Main>$(String[] args) in C:\Users\HP\Desktop\MyApp\WebApi\Program.cs:line 183
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2[Auth.Commands.SignIn.SignInCommand,Models.ServiceResult`1[Models.AuthResult]] Lifetime: Transient ImplementationType: Auth.Commands.SignIn.SignInCommandHandler': Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Oauth.JwtTokenManager'.
Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'MapsterMapper.IMapper' while attempting to activate 'Oauth.JwtTokenManager'.
I already Mapped ApplicationUserDto
How do I resolve this