Свободная проверка в ASP.NET Core - PullRequest
0 голосов
/ 19 февраля 2019

Я попытался заменить валидацию из моего предыдущего проекта на Asp.net MVC 4 на Asp.net Core.И есть некоторые проблемы.Поток в проекте Asp.net Core выглядит так:

Middleware => ControllerCTOR => FluValidator => Filter => Action

Также, когда некоторые из правил в FluValidator не сработали, это просто возвратный ответ с ошибкамичерез Middleware стека к клиенту.Но мне нужно иметь доступ к ModelState в фильтре или в действии.

Почему это не работает правильно?Или, если это действительно правильный поток, как заставить его идти глубже в 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();
    }
}

Контроллер

[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

    }
}

Фильтр

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            //logic
        }
    }
}

Типичный допустимый Json:

{ 
  "request": {
    "ru": "string",
    "accountNumber": "string",
    "currency": 1
  }
}

Типичный недействительный Json:

{ 
  "request": {
    "ru": "string",
    "accountNumber": "string",
    "currency": 0
  }
}

Когдавалюта НЕ ноль, она действительна, а фильтр достигает.Но когда он равен нулю, NotEmpty становится сбойным и поток возвращается.

Типичный ответ с действительным запросом:

{
  "elements": [
    {
      <object fields>
    }
  ],
  "messageText": "string",
  "messageNumber": 1
}

Типичный ответ с недействительным запросом (400 неправильных запросов):

{
  "Request.Currency": [
    "'Request. Currency' must not be empty."
  ]
} 

Ответы [ 2 ]

0 голосов
/ 20 февраля 2019

Я нашел решение.Проблема была в фильтре.Из-за OnActionExecuting метод запроса никогда не дойдет.После проверки, если есть какой-либо сбойный контекст правила, сразу переходит к OnResultExecution и возвращает ответ.

public class ValidateModelFilter : Attribute, IAsyncResultFilter
    {
        public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
        {
            if (!context.ModelState.IsValid)
                throw new InvalidParametersException(context.ModelState.StringErrors());

            await next();
        }
    }
0 голосов
/ 19 февраля 2019

Поток выполнения фактически достигает и ValidateModelAttribute, и действия, даже если модель недействительна.Но есть особый случай, когда свойство Request имеет значение null и CardInformationRequestValidator выдает исключение во время проверки.Например, когда валидатор пытается проверить это правило

RuleFor(x => x.Request.RU)
    .NotNull()
    .NotEmpty();

, он пытается получить RU значение свойства, но выбрасывает NullReferenceException, поскольку x.Request равно null.Таким образом, решение заключается в обновлении логики проверки

public CardInformationRequestValidator()
{
    RuleFor(x => x.Request)
        .NotNull()
        .DependentRules(() =>
        {
            RuleFor(x => x.Request.RU)
                .NotNull()
                .NotEmpty();

            RuleFor(x => x.Request.Currency)
                .NotNull()
                .NotEmpty();

            RuleFor(x => x.Request.AccountNumber)
                .NotNull()
                .NotEmpty();
        });
}

Подробнее в документах и на github .

...