我想将一些请求计时添加到对Nancy模块的请求的响应头中。我已经在RequestStartup中添加了一些前/后请求处理程序,并添加了头文件没有问题(下面的示例),一切都很好。我还在ApplicationStartup中添加了一个OnError处理程序,以捕获错误并返回一个很好的格式化Json响应。
pipelines.BeforeRequest += ctx =>
{
ctx.Items.Add("X-RequestStart", DateTime.Now.ToString());
return null;
};
pipelines.AfterRequest += ctx =>
{
var now = DateTime.Now;
try
{
//Not got around to forcing the culture on the datetime parsing yet...
var startTime = DateTime.Parse(ctx.Items["X-RequestStart"].ToString());
ctx.Response.Headers.Add("X-RequestStart", startTime.ToString(CultureInfo.InvariantCulture));
ctx.Response.Headers.Add("X-RequestComplete", now.ToString(CultureInfo.InvariantCulture));
ctx.Response.Headers.Add("X-RequestDuration", (now - startTime).ToString());
}
catch (Exception)
{
ctx.Response.Headers.Add("X-RequestComplete", now.ToString(CultureInfo.InvariantCulture));
}
};
pipelines.OnError += (ctx, exception) =>
{
return ErrorResponse.FromException(exception);
};
然而,我注意到的是,当我抛出错误时,不执行AfterRequest操作 - 因此我没有错误响应的时序头。我已经尝试将请求前/后处理移动到应用程序启动,但这也没有效果。
问题实际上分为两部分,首先,是否可以让框架在执行OnError操作后执行AfterRequest操作,或者是以防止此操作的方式设置管道,其次,应该请求操作之前/之后是RequestStartup或ApplicationStartup的一部分吗? ApplicationStartup对于错误处理似乎是明智的,而RequestStartup似乎对于与响应头进行交互是合理的,因为它应该基于每个请求,但是我不确定是否存在这样的约定,或者我的假设是否不正确。
答案 0 :(得分:0)
不幸的是,这在NancyFx中似乎不可能。我详细了解了源代码,特别是DefaultRequestDispatcher
。 Dispatch
捕获在路由处理期间抛出的任何异常,调用ResolveErrorResult
,然后从协商器获取响应。似乎没有可扩展点来修改以这种方式生成的响应。
在我看来,这是开发人员应该考虑解决的疏忽。
public async Task<Response> Dispatch(NancyContext context, CancellationToken cancellationToken)
{
// TODO - May need to make this run off context rather than response .. seems a bit icky currently
var resolveResult = this.Resolve(context);
context.Parameters = resolveResult.Parameters;
context.ResolvedRoute = resolveResult.Route;
try
{
context.Response = await ExecuteRoutePreReq(context, cancellationToken, resolveResult.Before)
.ConfigureAwait(false);
if(context.Response == null)
{
context.Response = await this.routeInvoker.Invoke(resolveResult.Route, cancellationToken,
resolveResult.Parameters, context)
.ConfigureAwait(false);
if (context.Request.Method.Equals("HEAD", StringComparison.OrdinalIgnoreCase))
{
context.Response = new HeadResponse(context.Response);
}
}
await this.ExecutePost(context, cancellationToken, resolveResult.After, resolveResult.OnError)
.ConfigureAwait(false);
}
catch(Exception ex)
{
context.Response = this.ResolveErrorResult(context, resolveResult.OnError, ex);
if (context.Response == null)
{
throw;
}
}
return context.Response;
}
private Response ResolveErrorResult(NancyContext context, Func<NancyContext, Exception, dynamic> resolveResultOnError, Exception exception)
{
if (resolveResultOnError != null)
{
var flattenedException = exception.FlattenInnerExceptions();
var result = resolveResultOnError.Invoke(context, flattenedException);
if (result != null)
{
return this.negotiator.NegotiateResponse(result, context);
}
}
return null;
}