[英]How to get a response from the Nancy Negotiator?
我有一個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<ProductRequest>();
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模塊引導程序中設置我的錯誤處理“一次”,並讓它捕獲已知/預期的異常並使用適當的響應對象返回它們。
一個簡單的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;
}
}
使用此方法,您可以重構模塊,只需拋出相應的異常即可調用正確的響應類型。 在這方面,您可以開始為API創建一套很好的標准。 一個例子是:
public class ProductsModule : NancyModule
{
public ProductsModule()
: base("/products")
{
Get["/product/{productid}"] = _ =>
{
var request = this.Bind<ProductRequest>();
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';
}
}
}
這對我來說感覺稍微清潔了,現在我在我的模塊中引入了一套標准。 :)
您可以考慮做的其他事情,在開發過程中特別有用,可以將完整的異常報告附加到錯誤響應對象的內容結果中。
一個基本的例子是:
result.Contents = responseStream =>
{
string errorBody = string.Format(
@"<html>
<head>
<title>Exception report</title>
</head>
<body>
<h1>{0}</h1>
<p>{1}</p>
</body>
</html>",
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<IResponseProcessor> processors, Nancy.Conventions.AcceptHeaderCoercionConventions coercionConventions)
{
var negotiator = new Negotiator(context)
.WithStatusCode(HttpStatusCode.BadRequest)
.WithReasonPhrase(exception.Message);
return new DefaultResponseNegotiator(processors, coercionConventions)
.NegotiateResponse(negotiator, context);
}
根據您的實現,更好的方法可能是將processors
和coercionConventions
作為類構造函數的參數,並允許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<IEnumerable<Nancy.Responses.Negotiation.IResponseProcessor>>();
var coercionConventions = container.Resolve<AcceptHeaderCoercionConventions>();
pipelines.OnError += (context, exception) =>
{
return exception.GetErrorResponse(context, responseProcessors, coercionConventions);
};
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.