二、自定义IActionResult
举个简单的例子,以第一节的第3个例子为例,该例通过 “return new JsonResult(new Book() { Code = "1001", Name = "ASP" })”返回了一个JsonResult。
返回的JSON值为:
{"code":"1001","name":"ASP"}
假如对于Book这种类型,希望用特殊的格式返回,例如这样的格式:
Book Code:[1001]|Book Name:<ASP>
可以通过自定义一个类似JsonResult的类来实现。代码如下:
public class BookResult : ActionResult { public BookResult(Book content) { Content = content; } public Book Content { get; set; } public string ContentType { get; set; } public int? StatusCode { get; set; } public override async Task ExecuteResultAsync(ActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var executor = context.HttpContext.RequestServices.GetRequiredService<IActionResultExecutor<BookResult>>(); await executor.ExecuteAsync(context, this); } }
定义了一个名为BookResult的类,为了方便继承了ActionResult。由于是为了处理Book类型,在构造函数中添加了Book类型的参数,并将该参数赋值给属性Content。重写ExecuteResultAsync方法,对应JsonResultExecutor,还需要自定义一个BookResultExecutor。代码如下:
public class BookResultExecutor : IActionResultExecutor<BookResult> { private const string DefaultContentType = "text/plain; charset=utf-8"; private readonly IHttpResponseStreamWriterFactory _httpResponseStreamWriterFactory; public BookResultExecutor(IHttpResponseStreamWriterFactory httpResponseStreamWriterFactory) { _httpResponseStreamWriterFactory = httpResponseStreamWriterFactory; } private static string FormatToString(Book book) { return string.Format("Book Code:[{0}]|Book Name:<{1}>", book.Code, book.Name); } /// <inheritdoc /> public virtual async Task ExecuteAsync(ActionContext context, BookResult result) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (result == null) { throw new ArgumentNullException(nameof(result)); } var response = context.HttpContext.Response; string resolvedContentType; Encoding resolvedContentTypeEncoding; ResponseContentTypeHelper.ResolveContentTypeAndEncoding( result.ContentType, response.ContentType, DefaultContentType, out resolvedContentType, out resolvedContentTypeEncoding); response.ContentType = resolvedContentType; if (result.StatusCode != null) { response.StatusCode = result.StatusCode.Value; } string content = FormatToString(result.Content); if (result.Content != null) { response.ContentLength = resolvedContentTypeEncoding.GetByteCount(content); using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding)) { await textWriter.WriteAsync(content); await textWriter.FlushAsync(); } } } }
这里定义了默认的ContentType 类型,采用了文本格式,即"text/plain; charset=utf-8",这会在请求结果的Header中出现。为了特殊说明这个格式,也可以自定义一个特殊类型,例如"text/book; charset=utf-8",这需要项目中提前约定好。定义了一个FormatToString方法用于将Book类型的数据格式化。最终将格式化的数据写入Response.Body中。
这个BookResultExecutor定义之后,需要在依赖注入中(Startup文件中的ConfigureServices方法)注册:
public void ConfigureServices(IServiceCollection services) { //省略部分代码 services.TryAddSingleton<IActionResultExecutor<BookResult>, BookResultExecutor>(); }
至此,这个自定义的BookResult就可以被使用了,例如下面代码所示的Action:
public BookResult GetBookResult() { return new BookResult(new Book() { Code = "1001", Name = "ASP" }); }
用Fiddler访问这个Action测试一下,返回结果如下:
Book Code:[1001]|Book Name:<ASP>
Header值:
Content-Length: 32 Content-Type: text/book; charset=utf-8
这是自定义了Content-Type的结果。