介紹
Web API為JSON和XML提供媒體類型格式化程序。框架默認將這些格式化程序插入管道中。客戶端可以在HTTP請求的Accept標頭中請求JSON或XML.
格式化數據這個東西,其實沒有什么最好的數據,要看各種場景,最適合才是最好的,不是說json就比xml好,容易解析什么的等。
廢話不多說了,概念的東西大家一百度一大堆。開始我們的正文吧,當然首先我們還是要創建一個WebAPI項目,不會創建請返回第一章:如何創建簡單的WebAPI項目
控制器的返回類型
特定類型:
首先我們最熟悉的就是特定類型了,比如stting或自定義對象類型等。就例如模版控制器的就是返回一個字符串數組類型:
[HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value2" }; }
這種的示例沒有已知條件,直接返回特定類型即可了 ,但是有些操作我們需要考慮已知的條件,這時候會返回多個返回結果。根據不同的條件返回對應結果,下面我們來看一下IActionResult 類型。
IActionResult 類型:
在多種情況條件返回多個不同結果時, 要支持此類操作,必須使用 IActionResult 或 ActionResult<T>。ActionResult類型表示多種的HTTP狀態碼。屬於此類別的一些常見返回類型包括:
BadRequestResult (400)
NotFoundResult (404)
OkObjectResult (200)
在返回多個類型的時候我們如何返回不同的類型哪,我們可以借助【ProducesResponseType】特性來幫助我們實現返回自定義多個類型。下面我們寫個簡單的get方法的同步和異步的示例:
同步示例:
返回兩個情況當id為5我給你正確返回,不是5我就找不到。當然實際情況肯定不是這個樣子,但是就是打一個找資源的例子,找到就返回,找不到就返回404,。
[HttpGet("{id}")] [ProducesResponseType(200, Type = typeof(Person))] [ProducesResponseType(404)] public IActionResult Get(int id) { if (id == 5) { return Ok(new Person { Id = "001", name = "姓名1", age = 18, Birthday = DateTime.Now, introduce = "介紹001" }); } else { return NotFound(); } }
下面是異步的方法:
[HttpGet("{id}")] [ProducesResponseType(200, Type = typeof(Person))] [ProducesResponseType(400)] public async Task<IActionResult> Get(int id) { if (id == 5) { await Task.Run(()=>System.Threading.Thread.Sleep(1000)); return Ok(new Person { Id = "001", name = "姓名1", age = 18, Birthday = DateTime.Now, introduce = "介紹001" }); } else { return BadRequest(); } }
CreatedAtAction方法:創建一個CreatedAtActionResult對象,該對象生成Status201Created響應;具體想要了解的可以查看官方文檔:CreatedAtAction方法介紹
下面我們看一下請求結果:
id不是5的時候返回找不到:

id為5的時候正常返回咱們的對象:


ActionResult<T> 類型:
這個類型是從ASP.NET Core 2.1引入的,所有使用前請看下版本哦。它支持返回從 ActionResult 派生的類型或返回特定類型。 ActionResult<T> 通過 IActionResult 類型可提供以下優勢:
- 可排除 [ProducesResponseType] 特性的
Type屬性 - 隱式強制轉換運算符支持將
T和ActionResult均轉換為ActionResult<T>。 將T轉換為 ObjectResult,也就是將return new ObjectResult(T);簡化為return T,什么個意思哪,說白了就是在定義的時候指定了類型直接return就可以了。
返回響應補充:
- OK:創建一個OkResult對象,該對象生成一個空的Status200OK響應。
- NoContentResult:沒有內容,為響應創建的NoContentResult對象
-
NotFound():沒有找到,為響應創建的NotFoundResult。
-
PhysicalFile(string【文件的路徑】,string【內容類型】):返回由
physicalPath(Status200OK)指定的文件。 - Redirect(string【url】):重定向,為響應創建的RedirectResult。
- StatusCode(int【返回的狀態碼】,object【值】):自定義返回狀態,切附帶返回值。其實是為響應創建了ObjectResult對象。
我就寫部分常用的其他的有興趣可以去官網了解一下:返回狀態相應
自定義格式化程序
我們都知道WebAPI因為MVC的內置所以默認支持了json,xml和文本格式。那么我們想使用其他格式怎么辦哪,微軟總是不會讓我們失望,我們可以自定義啊。
首先創建自定義格式化程序大致步驟:
- 從相應的基類中派生類。
- 在構造函數中指定有效的媒體類型和編碼。
- 重寫
CanReadType/CanWriteType方法 - 重寫
ReadRequestBodyAsync/WriteResponseBodyAsync方法
從相應的基類中派生:
從那些類中派生官方給的解釋是;
對於文本媒體類型(例如,vCard),從 TextInputFormatter 或 TextOutputFormatter 基類派生。
對於二進制類型,從 InputFormatter 或 OutputFormatter 基類派生。
例如官方示例:
public class VcardOutputFormatter : TextOutputFormatter
指定有效的媒體類型和編碼
在構造函數中,通過添加到 SupportedMediaTypes 和 SupportedEncodings 集合來指定有效的媒體類型和編碼。
public VcardOutputFormatter() { SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard")); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); }
重寫 CanReadType/CanWriteType
通過重寫 CanReadType 或 CanWriteType 方法,指定可反序列化為或從其序列化的類型。 例如,可能只能從 Contact 類型創建 vCard 文本,反之亦然。
protected override bool CanWriteType(Type type) { if (typeof(Contact).IsAssignableFrom(type) || typeof(IEnumerable<Contact>).IsAssignableFrom(type)) { return base.CanWriteType(type); } return false; }
CanWriteResult方法不一定必須重寫,但是有時候確實必須的,必須重寫官方給的解釋是;
- 操作方法返回模型類。
- 具有可能在運行時返回的派生類。
- 需要知道操作在運行時返回了哪個派生類。
簡單的意思是如果你返回的類型是父類的話,但是實際返回值可能存在子類型的返回且子類型為多個。但是你僅僅希望處理其中一個子類型的返回。這個時候可以使用CanWriteResult提供的上下文來檢查對象類型。
重寫 ReadRequestBodyAsync/WriteResponseBodyAsync
實際的反序列化或序列化工作在 ReadRequestBodyAsync 或 WriteResponseBodyAsync 中執行。 以下示例中突出顯示的行展示了如何從依賴關系注入容器中獲取服務(不能從構造函數參數中獲取它們)
public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) { IServiceProvider serviceProvider = context.HttpContext.RequestServices; var logger = serviceProvider.GetService(typeof(ILogger<VcardOutputFormatter>)) as ILogger; var response = context.HttpContext.Response; var buffer = new StringBuilder(); if (context.Object is IEnumerable<Contact>) { foreach (Contact contact in context.Object as IEnumerable<Contact>) { FormatVcard(buffer, contact, logger); } } else { var contact = context.Object as Contact; FormatVcard(buffer, contact, logger); } return response.WriteAsync(buffer.ToString()); }
private static void FormatVcard(StringBuilder buffer, Contact contact, ILogger logger) { buffer.AppendLine("BEGIN:VCARD"); buffer.AppendLine("VERSION:2.1"); buffer.AppendFormat($"N:{contact.LastName};{contact.FirstName}\r\n"); buffer.AppendFormat($"FN:{contact.FirstName} {contact.LastName}\r\n"); buffer.AppendFormat($"UID:{contact.ID}\r\n"); buffer.AppendLine("END:VCARD"); logger.LogInformation($"Writing {contact.FirstName} {contact.LastName}"); }
