I tried to replace validation from my previous project on Asp.net MVC 4 to Asp.net Core. And have some problems. The flow in Asp.net Core project like that:
Middleware => ControllerCTOR => FluValidator => Filter => Action
Also when some of Rules in FluValidator failed it's just return response with errors through Middleware stack to client. But I need to have access to ModelState in Filter or in Action.
Why this don`t work correct? Or, if it's actually right flow, how to make it go deeper to Action?
Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(typeof(ValidateModelAttribute));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddNLog();
env.ConfigureNLog("nlog.config");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "CorpLight API V1");
});
app.UseMiddleware<RequestResponseLoggingMiddleware>();
app.UseMiddleware<ErrorHandlingMiddleware>();
app.UseMiddleware<AuthenticateMiddleware>();
app.UseMvc();
}
Middleware
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
Validator
public class CardInformationRequestValidator : AbstractValidator<RequestModel<CardInformationRequest>>
{
public CardInformationRequestValidator()
{
RuleFor(x => x.Request.RU)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.Currency)
.NotNull()
.NotEmpty();
RuleFor(x => x.Request.AccountNumber)
.NotNull()
.NotEmpty();
}
}
Controller
[Route("api/[controller]")]
[ApiController]
public class CardController : ControllerBase
{
private readonly ICardRepo _cardRepo;
private readonly IMapper _mapper;
public CardController(ICardRepo cardRepo, IMapper mapper)
{
_cardRepo = cardRepo;
_mapper = mapper;
}
[HttpPost]
public async Task<MessageWithElements<CardInformation, CardInfo>> CardInformations(RequestModel<CardInformationRequest> request)
{
if (!ModelState.IsValid)
throw new InvalidParametersException($"can't be empty");
//logic
}
}
Filter
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
//logic
}
}
}
Typical Valid Json:
{
"request": {
"ru": "string",
"accountNumber": "string",
"currency": 1
}
}
Typical Invalid Json:
{
"request": {
"ru": "string",
"accountNumber": "string",
"currency": 0
}
}
When currency NOT zero it's valid, and reach filter. But when it's zero, NotEmpty become failded and flow go back.
Typical response with Valid request:
{
"elements": [
{
<object fields>
}
],
"messageText": "string",
"messageNumber": 1
}
Typical response with Invalid request (400 Bad Request):
{
"Request.Currency": [
"'Request. Currency' must not be empty."
]
}
app.UseYourMiddleware