如何得到南希谈判员的回应?

我有一个NancyContext ,我需要根据请求的正确内容协商器获取一个Response 。 我想我可以使用Nancy的Negotiator类来添加模型,设置状态等等。 但是,我需要返回一个Response的子类型。 那么,我可以使用什么来使用Negotiator来构建响应?

这是我的方法:

 public Response ConvertToHttpResponse(Exception exception, NancyContext context) { var negotiator = new Negotiator(context) .WithStatusCode(HttpStatusCode.BadRequest) .WithReasonPhrase(exception.Message); return ???; } 

我个人更喜欢使用Nancy协商器返回“Happy Path”结果(即view / jsondto返回),然后返回vanilla nancy Response对象以查找可能发生的任何错误。

这样做的一种方法是直接在模块中返回错误,例如:

 public class ProductsModule : NancyModule { public ProductsModule() : base("/products") { Get["/product/{productid}"] = _ => { var request = this.Bind(); var product = ProductRepository.GetById(request.ProductId); if (product == null) { var error = new Response(); error.StatusCode = HttpStatusCode.BadRequest; error.ReasonPhrase = "Invalid product identifier."; return error; } var user = UserRepository.GetCurrentUser(); if (false == user.CanView(product)) { var error = new Response(); error.StatusCode = HttpStatusCode.Unauthorized; error.ReasonPhrase = "User has insufficient privileges."; return error; } var productDto = CreateProductDto(product); var htmlDto = new { Product = productDto, RelatedProducts = GetRelatedProductsDto(product) }; return Negotiate .WithAllowedMediaRange(MediaRange.FromString("text/html")) .WithAllowedMediaRange(MediaRange.FromString("application/json")) .WithModel(htmlDto) // Model for 'text/html' .WithMediaRangeModel( MediaRange.FromString("application/json"), productDto); // Model for 'application/json'; } } } 

这可能会变得相当混乱。 我首选的方法是在我的Nancy模块引导程序中设置我的error handling“一次”,让它捕获已知/预期的exception并使用适当的响应对象返回它们。

一个简单的bootrapper配置示例可能是:

 public class MyNancyBootstrapper : DefaultNancyBootstrapper { protected override void ApplicationStartup( TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); // Register the custom exceptions handler. pipelines.OnError += (ctx, err) => HandleExceptions(err, ctx); ; } private static Response HandleExceptions(Exception err, NancyContext ctx) { var result = new Response(); result.ReasonPhrase = err.Message; if (err is NotImplementedException) { result.StatusCode = HttpStatusCode.NotImplemented; } else if (err is UnauthorizedAccessException) { result.StatusCode = HttpStatusCode.Unauthorized; } else if (err is ArgumentException) { result.StatusCode = HttpStatusCode.BadRequest; } else { // An unexpected exception occurred! result.StatusCode = HttpStatusCode.InternalServerError; } return result; } } 

使用此方法,您可以重构模块,只需抛出相应的exception即可调用正确的响应类型。 在这方面,您可以开始为API创建一套很好的标准。 一个例子是:

 public class ProductsModule : NancyModule { public ProductsModule() : base("/products") { Get["/product/{productid}"] = _ => { var request = this.Bind(); var product = ProductRepository.GetById(request.ProductId); if (product == null) { throw new ArgumentException( "Invalid product identifier."); } var user = UserRepository.GetCurrentUser(); if (false == user.CanView(product)) { throw new UnauthorizedAccessException( "User has insufficient privileges."); } var productDto = CreateProductDto(product); var htmlDto = new { Product = productDto, RelatedProducts = GetRelatedProductsDto(product) }; return Negotiate .WithAllowedMediaRange(MediaRange.FromString("text/html")) .WithAllowedMediaRange(MediaRange.FromString("application/json")) .WithModel(htmlDto) // Model for 'text/html' .WithMediaRangeModel( MediaRange.FromString("application/json"), productDto); // Model for 'application/json'; } } } 

这对我来说感觉稍微清洁了,现在我在我的模块中引入了一套标准。 🙂


您可以考虑做的其他事情,在开发过程中特别有用,可以将完整的exception报告附加到错误响应对象的内容结果中。

一个基本的例子是:

 result.Contents = responseStream => { string errorBody = string.Format( @"  Exception report   

{0}

{1}

", ex.Message, ex.StackTrace); // convert error to stream and copy to response stream var byteArray = Encoding.UTF8.GetBytes(errorBody); using (var errorStream = new MemoryStream(byteArray)) { errorStream.CopyTo(responseStream); } }

同样,这只是一个非常基本的说明性示例,您必须确定它是否适合您的解决方案,然后对其进行扩展。

根据您的代码示例,这是一种可能的方法:

 public Response ConvertToHttpResponse(Exception exception, NancyContext context, IEnumerable processors, Nancy.Conventions.AcceptHeaderCoercionConventions coercionConventions) { var negotiator = new Negotiator(context) .WithStatusCode(HttpStatusCode.BadRequest) .WithReasonPhrase(exception.Message); return new DefaultResponseNegotiator(processors, coercionConventions) .NegotiateResponse(negotiator, context); } 

根据您的实现,更好的方法可能是将processorscoercionConventions作为类构造函数的参数,并允许IoC容器正常解析它们。 但是,就我而言,我在我的引导程序中解决了它们,并将它们提供给我创建的扩展方法,用于将Exception实例与XML或JSON响应进行协商。

 protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { // Resolving outside the lambda because no more components will be registered at this point. var responseProcessors = container.Resolve>(); var coercionConventions = container.Resolve(); pipelines.OnError += (context, exception) => { return exception.GetErrorResponse(context, responseProcessors, coercionConventions); }; }