【WebAPI No.5】Core WebAPI中的自定義格式化

介紹

Web API爲JSON和XML提供媒體類型格式化程序。框架默認將這些格式化程序插入管道中。客戶端能夠在HTTP請求的Accept標頭中請求JSON或XML.html

格式化數據這個東西,其實沒有什麼最好的數據,要看各類場景,最適合纔是最好的,不是說json就比xml好,容易解析什麼的等。git

廢話很少說了,概念的東西你們一百度一大堆。開始咱們的正文吧,固然首先咱們仍是要建立一個WebAPI項目,不會建立請返回第一章:如何建立簡單的WebAPI項目github

控制器的返回類型

特定類型:

首先咱們最熟悉的就是特定類型了,好比stting或自定義對象類型等。就例如模版控制器的就是返回一個字符串數組類型:web

     [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return new string[] { "value1", "value2" };
        }

這種的示例沒有已知條件,直接返回特定類型便可了 ,可是有些操做咱們須要考慮已知的條件,這時候會返回多個返回結果。根據不一樣的條件返回對應結果,下面咱們來看一下IActionResult 類型。json

IActionResult 類型:

在多種狀況條件返回多個不一樣結果時, 要支持此類操做,必須使用 IActionResult 或 ActionResult<T>。ActionResult類型表示多種的HTTP狀態碼。屬於此類別的一些常見返回類型包括:api

BadRequestResult (400)數組

NotFoundResult (404) mvc

OkObjectResult (200)app

在返回多個類型的時候咱們如何返回不一樣的類型哪,咱們能夠藉助【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();
            }
            
        }
View Code

下面是異步的方法:

 [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();
            }
            
        }
View Code

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【內容類型】):返回由physicalPathStatus200OK指定的文件。

  • 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());
}
View Code
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}");
}
View Code

 官方示例DOME下載

 傳送門

WebApi系列文章目錄介紹

相關文章
相關標籤/搜索