在OnResultExecuting中可以通过设置context.Cancel = true;取消后面的工作的执行。
public void OnResultExecuting(ResultExecutingContext context) { //用于验证的代码略 context.Cancel = true; Debug.WriteLine("HomeController=======>OnResultExecuting"); }
再看输出结果就至于的输出了
HomeController=======>OnResultExecuting
同时返回结果也不再是JSON值了,返回结果以及Content-Type全部为空。所以这样设置后,IActionResult以及OnResultExecuted都不再被执行。
在这里除了可以做一些IActionResult执行之前的验证,还可以对HttpContext.Response做一些简单的操作,例如添加个Header值:
public void OnResultExecuting(ResultExecutingContext context) { context.HttpContext.Response.Headers.Add("version", "1.2"); Debug.WriteLine("HomeController=======>OnResultExecuting"); }
除了正常返回JSON结果外,Header中会出现新添加的version
Content-Type: application/json; charset=utf-8 version: 1.2
再看一下OnResultExecuted方法,它是在IActionResult执行之后执行。因为在这个方法执行的时候,请求结果已经发送给请求的客户端了,所以在这里可以做一些日志类的操作。举个例子,假如在这个方法中产生了异常:
public void OnResultExecuted(ResultExecutedContext context) { throw new Exception("exception"); Debug.WriteLine("HomeController=======>OnResultExecuted"); }
请求结果依然会返回正常的JSON,但从输出结果中看到是不一样的
HomeController=======>OnResultExecuting …… System.Exception: exception
发生异常,后面的Debug输出没有 执行。但却将正确的结果返回给了客户端。
Result Filter介绍完了,看一下核心的IActionResult的执行。
4. IActionResult的执行
在ResourceInvoker的case State.ResultInside阶段会调用IActionResult的执行方法InvokeResultAsync。该方法中参数IActionResult result会被调用ExecuteResultAsync方法执行。
protected virtual async Task InvokeResultAsync(IActionResult result) { var actionContext = _actionContext; _diagnosticSource.BeforeActionResult(actionContext, result); _logger.BeforeExecutingActionResult(result); try { await result.ExecuteResultAsync(actionContext); } finally { _diagnosticSource.AfterActionResult(actionContext, result); _logger.AfterExecutingActionResult(result); } }
由图 2可知,虽然所有类型的Action的结果都被转换成了IActionResult,但它们本质上还是有区别的。所以这个IActionResult类型的参数result实际上可能是JsonResult、ViewResult、EmptyResult等具体类型。下面依然以第一节JSON的例子为例来看,它返回了一个JsonResult。在这里就会调用JsonResult的ExecuteResultAsync方法,JsonResult的代码如下:
public class JsonResult : ActionResult, IStatusCodeActionResult { //部分代码略 public override Task ExecuteResultAsync(ActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var services = context.HttpContext.RequestServices; var executor = services.GetRequiredService<JsonResultExecutor>(); return executor.ExecuteAsync(context, this); } }
在它的ExecuteResultAsync方法中会获取依赖注入中设置的JsonResultExecutor,由JsonResultExecutor来调用ExecuteAsync方法执行后面的工作。JsonResultExecutor的代码如下:
public class JsonResultExecutor { //部分代码略 public virtual async Task ExecuteAsync(ActionContext context, JsonResult result) { //验证代码略 var response = context.HttpContext.Response; ResponseContentTypeHelper.ResolveContentTypeAndEncoding( result.ContentType, response.ContentType, DefaultContentType, out var resolvedContentType, out var resolvedContentTypeEncoding); response.ContentType = resolvedContentType; if (result.StatusCode != null) { response.StatusCode = result.StatusCode.Value; } var serializerSettings = result.SerializerSettings ?? Options.SerializerSettings; Logger.JsonResultExecuting(result.Value); using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding)) { using (var jsonWriter = new JsonTextWriter(writer)) { jsonWriter.ArrayPool = _charPool; jsonWriter.CloseOutput = false; jsonWriter.AutoCompleteOnClose = false; var jsonSerializer = JsonSerializer.Create(serializerSettings); jsonSerializer.Serialize(jsonWriter, result.Value); } // Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's // buffers. This is better than just letting dispose handle it (which would result in a synchronous write). await writer.FlushAsync(); } } }
JsonResultExecutor的ExecuteAsync方法的作用就是将JsonResult中的值转换成JSON串并写入context.HttpContext.Response. Body中。至此JsonResult执行完毕。
ViewResult会有对应的ViewExecutor来执行,会通过相应的规则生成一个 Html页面。
而EmptyResult的ExecuteResult方法为空,所以不会返回任何内容。
public class EmptyResult : ActionResult { /// <inheritdoc /> public override void ExecuteResult(ActionContext context) { } }
5. 下集预告
对于以上几种类型返回结果的格式是固定的,JsonResult就会返回JSON格式,ViewResult就会返回Html格式。
但是从第一节的例子可知,string类型会返回string类型的字符串,而Book这样的实体类型却会返回JSON。
由图 2可知这两种类型在执行完毕后,都被封装成了ObjectResult,那么ObjectResult在执行的时候又是如何被转换成string和JSON两种格式的呢?
下一章继续这个话题。