gpt4 book ai didi

c# - 如何将 FluentValidation 库与 MediatR IPipelineBehavior 一起使用?

转载 作者:行者123 更新时间:2023-12-04 17:44:56 25 4
gpt4 key购买 nike

我开始探索 MediatR但是当从 Postman 调用 GET 端点时遇到问题,API 返回 500 内部服务器错误。而且,我认为 FluentValidation pipeline 的实现导致了这个问题。也许,我的实现不正确,或者我可能误解了。这是代码,请有人看一下!

来自 Controller 的代码。

using BusinessLayer.Test.Commands;
using BusinessLayer.Test.Queries;
using MediatR;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using System.Threading.Tasks;

namespace MediatRTestApi.Controllers
{
[Route("api/v1/[controller]")]
public class TestsController : Controller
{
readonly IMediator _mediator;

public TestsController(IMediator mediator)
{
_mediator = mediator;
}

[HttpGet]
public async Task<IActionResult> FindList([FromQuery]TestQueryRequest model)
{
var results = await _mediator.Send(model);

return new OkObjectResult(new { results });
}

[HttpPost]
public async Task<IActionResult> Post([FromBody]TestAddRequest model)
{
var response = await _mediator.Send(model).ConfigureAwait(false);

if (response.Errors.Any())
{
return BadRequest(response.Errors);
}

return Ok(response.Result);
}
}
}

处理程序代码。

using BusinessLayer.Pipelines;
using DataStores;
using MediatR;
using Microsoft.Extensions.Configuration;
using System.Threading;
using System.Threading.Tasks;

namespace BusinessLayer.Test.Commands
{
public class TestAddHandler : IRequestHandler<TestAddRequest, Response>
{
readonly string _dbConnectionString;

public TestAddHandler(IConfiguration configuration)
{
_dbConnectionString = configuration.GetSection("connectionStrings:oltpConnectionString").Value;
}

public async Task<Response> Handle(TestAddRequest request, CancellationToken cancellationToken)
{
var poco = ParseRequest(request);
await AddTestRecord(poco);

return new Response("Record is added.");
}

async Task AddTestRecord(TestPoco poco)
{
var store = await new TestStore(_dbConnectionString).InsertAsync(poco);
}

TestPoco ParseRequest(TestAddRequest request)
{
return new TestPoco() { TestName = request.TestName, IsActive = true };
}
}
}

请求和响应代码。

using BusinessLayer.Pipelines;
using FluentValidation;
using MediatR;

namespace BusinessLayer.Test.Commands
{
public class TestAddRequest : IRequest<Response>
{
public string TestName { get; set; }
}

public class TestAddRequestValidator : AbstractValidator<TestAddRequest>
{
readonly IMediator _mediator;

public TestAddRequestValidator(IMediator mediator)
{
_mediator = mediator;

RuleFor(x => x.TestName).NotEmpty();
}
}
}

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace BusinessLayer.Pipelines
{
public class Response
{
readonly IList<string> _messages = new List<string>();

public IEnumerable<string> Errors { get; }
public object Result { get; }

public Response() => Errors = new ReadOnlyCollection<string>(_messages);

public Response(object result) : this()
{
Result = result;
}

public Response AddError(string message)
{
_messages.Add(message);
return this;
}
}
}

FluentValidation 管道的代码。

using FluentValidation;
using FluentValidation.Results;
using MediatR;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace BusinessLayer.Pipelines
{
public class FailFastRequestBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse> where TResponse : Response
{
private readonly IEnumerable<IValidator> _validators;

public FailFastRequestBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}

public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var failures = _validators
.Select(v => v.Validate(request))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();

return failures.Any()
? Errors(failures)
: next();
}

private static Task<TResponse> Errors(IEnumerable<ValidationFailure> failures)
{
var response = new Response();

foreach (var failure in failures)
{
response.AddError(failure.ErrorMessage);
}

return Task.FromResult(response as TResponse);
}
}
}

像这样在 Startup.cs 中注册管道:

services.AddTransient(typeof(IPipelineBehavior<,>), typeof(FailFastRequestBehavior<,>));

当我执行命令时,出现以下错误。

    fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NullReferenceException: Object reference not set to an instance of an object.
at MediatR.Internal.RequestHandlerWrapperImpl`2.<>c__DisplayClass0_1.<Handle>b__2()
at MediatR.Internal.RequestHandlerWrapperImpl`2.Handle(IRequest`1 request, CancellationToken cancellationToken, ServiceFactory serviceFactory)
at MediatR.Mediator.Send[TResponse](IRequest`1 request, CancellationToken cancellationToken)
at MediatRTestApi.Controllers.TestsController.FindList(TestQueryRequest model) in C:\Test\MediatRTestApi\MediatRTestApi\Controllers\TestsController.cs:line 23
at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at System.Threading.Tasks.ValueTask`1.get_Result()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ExceptionContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 507.141ms 500 text/html; charset=utf-8

请注意,当我在 startup.cs 中分离 FluentValidation 管道时,我没有得到问题,当然,Post 方法不会执行它的验证。

最佳答案

public class RequestValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;

public RequestValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}

public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var context = new ValidationContext(request);

var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();

if (failures.Count != 0)
{
throw new Core.Exceptions.ValidationException(failures);
}

return next();
}
}


// Ioc container config.
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestValidationBehavior<,>));

关于c# - 如何将 FluentValidation 库与 MediatR IPipelineBehavior 一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52517280/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com