我正在尝试按照Jimmy Bogard的
this post来实现一个中介管道,这样我就可以使用前/后请求处理程序来完成一些工作.从这篇文章的评论我来到这个
github gist.我不太明白如何把所有这些挂钩,所以这是我的第一次去.仅供参考 – 我正在使用Autofac进行DI和Web Api 2.遵循CQRS,这是一个查询.
public class GetAccountRequest : IAsyncRequest<GetAccountResponse> { public int Id { get; set; } } //try using fluent validation public class GetAccountRequestValidationHandler : AbstractValidator<GetAccountRequest>,IAsyncPreRequestHandler<GetAccountRequest> { public GetAccountRequestValidationHandler() { RuleFor(m => m.Id).GreaterThan(0).WithMessage("Please specify an id."); } public Task Handle(GetAccountRequest request) { Debug.WriteLine("GetAccountPreProcessor Handler"); return Task.FromResult(true); } } public class GetAccountResponse { public int AccountId { get; set; } public string Name { get; set; } public string AccountNumber { get; set; } public string Nickname { get; set; } public string PhoneNumber { get; set; } public List<OrderAckNotification> OrderAckNotifications { get; set; } public class OrderAckNotification { public int Id { get; set; } public bool IsDefault { get; set; } public string Description { get; set; } public string Type { get; set; } } }
GetAccountRequestHandler:
public class GetAccountRequestHandler : IAsyncRequestHandler<GetAccountRequest,GetAccountResponse> { private readonly IRedStripeDbContext _dbContext; public GetAccountRequestHandler(IRedStripeDbContext redStripeDbContext) { _dbContext = redStripeDbContext; } public async Task<GetAccountResponse> Handle(GetAccountRequest message) { //some mapping code here.. omitted for brevity Mapper.AssertConfigurationIsValid(); return await _dbContext.Accounts.Where(a => a.AccountId == message.Id) .ProjectToSingleOrDefaultAsync<GetAccountResponse>(); }
这是显示HttpGet的当前web api 2控制器.
[RoutePrefix("api/Accounts")] public class AccountsController : ApiController { private readonly IMediator _mediator; public AccountsController(IMediator mediator) { _mediator = mediator; } // GET: api/Accounts/2 [Route("{id:int}")] [HttpGet] public async Task<IHttpActionResult> GetById([FromUri] GetAccountRequest request) { var model = await _mediator.SendAsync<GetAccountResponse>(request); return Ok(model); } }
最后这里是依赖解析代码:
public void Configuration(IAppBuilder app) { var config = new HttpConfiguration(); ConfigureDependencyInjection(app,config); WebApiConfig.Register(config); app.UseWebApi(config); } private static void ConfigureDependencyInjection(IAppBuilder app,HttpConfiguration config) { var builder = new ContainerBuilder(); builder.RegisterSource(new ContravariantRegistrationSource()); builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces(); builder.Register<SingleInstanceFactory>(ctx => { var c = ctx.Resolve<IComponentContext>(); return t => c.Resolve(t); }); builder.Register<MultiInstanceFactory>(ctx => { var c = ctx.Resolve<IComponentContext>(); return t => (IEnumerable<object>)c.Resolve( typeof(IEnumerable<>).MakeGenericType(t)); }); //register all pre handlers builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) .As(type => type.GetInterfaces() .Where(t => t.IsClosedTypeOf(typeof(IAsyncPreRequestHandler<>)))); //register all post handlers builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) .As(type => type.GetInterfaces() .Where(t => t.IsClosedTypeOf(typeof(IAsyncPostRequestHandler<,>)))); //register all handlers builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) .As(type => type.GetInterfaces() .Where(t => t.IsClosedTypeOf(typeof(IAsyncRequestHandler<,>))) .Select(t => new KeyedService("asyncRequestHandler",t))); //register pipeline decorator builder.RegisterGenericDecorator(typeof(AsyncMediatorPipeline<,>),typeof(IAsyncRequestHandler<,"asyncRequestHandler"); // Register Web API controller in executing assembly. builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest(); //register RedStripeDbContext builder.RegisterType<RedStripeDbContext>().As<IRedStripeDbContext>() .InstancePerRequest(); builder.RegisterType<AutofacServiceLocator>().AsImplementedInterfaces(); var container = builder.Build(); config.DependencyResolver = new AutofacWebApiDependencyResolver(container); // This should be the first middleware added to the IAppBuilder. app.UseAutofacMiddleware(container); // Make sure the Autofac lifetime scope is passed to Web API. app.UseAutofacWebApi(config); }
我正在进入GetAccountRequestValidationHandler.但是,当验证失败(传递了id为0)时,如何抛出异常或停止执行管道?如何返回.WithMessage?
解决方法
我也一直在努力解决这个问题.似乎有两个/三个选项:
使用预处理程序……
1)您可以将错误加载到请求中,并让主处理程序在处理命令/查询之前检查错误
要么
2)让预处理程序抛出异常.对于这种做法,似乎存在一些分歧.一方面,感觉就像管理具有异常的控制流一样,但“专业”阵营认为客户端应该负责发送有效的命令. IE浏览器.在让用户单击“创建帐户”之前,它可以发送ajax查询以确认用户名可用.在这种情况下,违反此规则的异常将归因于竞争条件.
将验证处理程序直接放入管道中.
我相信这更像是@jbogard所想的.
我目前没有使用它,但我已经勾勒出了这可能是什么样子 – 那里可能有更好的例子,当然你要如何定义和处理事情可能会有所不同.它的要点是,它是管道的一部分,验证运行器可以返回调用者,而不会调用主处理程序.
public class AsyncValidationPipeline<TRequest,TResponse> : IAsyncRequestHandler<TRequest,TResponse> where TRequest : IAsyncRequest<TResponse> { private IAsyncRequestHandler<TRequest,TResponse> _inner; private IValidator<TRequest>[] _validators; public AsyncValidationPipeline(IAsyncRequestHandler<TRequest,TResponse> inner,IValidator<TRequest>[] validators) { _inner = inner; _validators = validators; } public Task<TResponse> Handle(TRequest message) { List<string> errors = new List<string>(); if (_validators != null && _validators.Any()) { errors = _validators.Where(v => v.Fails(message)) .Select(v => v.ErrorMessage); } if (errors.Any()) { throw new ValidationException(errors); } return _inner.Handle(message); } }
这是使用AutoFac挂钩的代码:
//register all pre handlers builder.RegisterAssemblyTypes(assembliesToScan) .AsClosedTypesOf(typeof(IAsyncPreRequestHandler<>)); //register all post handlers builder.RegisterAssemblyTypes(assembliesToScan) .AsClosedTypesOf(typeof(IAsyncPostRequestHandler<,>)); const string handlerKey = "async-service-handlers"; const string pipelineKey = "async-service-pipelines"; // Request/Response for Query builder.RegisterAssemblyTypes(assembliesToScan) .AsKeyedClosedTypesOf(typeof(IAsyncRequestHandler<,handlerKey) ; // Decorate All Services with our Pipeline //builder.RegisterGenericDecorator(typeof(MediatorPipeline<,typeof(IRequestHandler<,fromKey: "service-handlers",toKey: "pipeline-handlers"); builder.RegisterGenericDecorator(typeof(AsyncMediatorPipeline<,fromKey: handlerKey,toKey: pipelineKey); // Decorate All Pipelines with our Validator builder.RegisterGenericDecorator(typeof(AsyncValidationHandler<,fromKey: pipelineKey);//,toKey: "async-validator-handlers"); // Add as many pipelines as you want,but the to/from keys must be kept in order and unique
希望这可以帮助….