【ASP.NET Core】設定Web API 響應的資料格式——Produces 特性篇

東邪獨孤發表於2022-02-06

開春首文,今天老周就跟各位大夥伴們聊一個很簡單的話題:怎麼設定API響應的資料格式。

說本質一點,就是設定所返回內容的 MIME 型別(Content-Type 頭)。當然了,我們們不會使用在HTTP管道中插入中介軟體的方式來解決,因為:

A、這樣做會導致所有傳入傳出的HTTP訊息都被修改;

B、這樣會毀壞API應用的設計規範,弄得不倫不類、禮崩樂壞、不堪入目。

所以,今天的主角是一個特性類(Attribute),它的大名叫 ProducesAttribute,位於 Microsoft.AspNetCore.Mvc 名稱空間下。這麼一介紹,你肯定能找到它。

根據定義,該特性類可用於:類、方法。說得再直接一點,就是用於 Controller類 和 Action方法。

這個特性類用於 設定API返回資料的MIME型別,嗯,也就是所謂的格式了。最人性化最簡單的使用方法就是這樣:

    [Produces("text/html")]
    [Produces("audio/wav")]
    [Produces("image/png")]
    [Produces("application/octet-stream")]

就是這樣,你希望返回的是啥東西,就用 Content-Type 字串來指定。

 

馬上,立刻,現在,就給大夥兒演示一個例子,讓 API 返回 text/json 型別的資料。因為預設情況下,API 返回資料使用 application/json 格式,所以,我們們要改為 text/json,就得用 Produces 特性。

首先,新建一個空的 ASP.NET Core 應用專案。老周喜歡空模板,易於 DIY,可折騰性強。

然後,在 Program.cs 檔案中註冊與 MVC 控制器有關的服務,以及Map一下相關中介軟體。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();

app.MapControllers();   //這一句不要忘了

app.Run();

接著,新建一個類,或者在“新建項”中選擇空的 API 控制器。

    [Route("api/[controller]")]
    [ApiController]
    public class Demo : ControllerBase
    {
        ……
    }

Route 特性指定訪問這個控制器的 URL,[controller] 是個佔位符,訪問時用實際的控制器名來替換。比如,這裡的控制器的名字是 Demo,訪問時的URL就是 http://somehost/api/demo/xxxx。不過,這裡老周的命名不太規範,規範的命名應該是 DemoController。只是老周嫌它的字尾太長。

其實 API 和 MVC 的控制器實現起來一樣,但 API 沒有檢視,所以類繼承時,基類可以用 ControllerBase 類而不是 Controller 類。另外,在類上面加一個 ApiController 特性,表明這個 Demo 類是作為 API 控制器用的,並且它的派生類都作為 API 控制器。

好,我們先實現兩個 Action。

        [Route("getbt")]
        [HttpGet]
        public string GetWTF() => "What the bitch";

        [Route("getak")]
        [HttpGet]
        public IDictionary<string, int> GetAK()
        {
            // 返回一個類例項和返回字典物件
            // 其JSON結構差不多
            // 此處為了簡單,直接用字典
            return new Dictionary<string, int>
            {
                ["item1"] = 10,
                ["item2"] = 49
            };
        }

Action 方法上指定的 Route 是相對於控制器類的 Route 的,即 /api/demo/getbt、/api/demo/getak。

這個相信各位看得懂,不用過多解釋,看不懂的肯定是因為你太謙虛了。

執行一下這個示例,直接通過瀏覽器的開發人員工具檢視,得知:

第一個 action 返回的 string 型別,因此預設選用 text/plain 格式(普通文字)。

 

 

第二個 action 返回的是字典物件,預設選擇 application/json 格式。

 

 

現在,把 Produces 特性用上,使其返回的資料變為 text/json 格式。

    [Route("api/[controller]")]
    [ApiController]
    [Produces("text/json")]
    public class Demo : ControllerBase
    {
        ……
    }

再次執行,從瀏覽器的開發人員工具中檢視HTTP訊息。

 

 

不過,你得小心!如果你指定的格式與 API 所返回的物件無法相容,就會崩盤。比如,把上面的 getak 改成這樣:

        [Route("getak")]
        [HttpGet]
        [Produces("text/plain")]
        public IDictionary<string, int> GetAK()
        {
            ……
        }

雖然 Demo 控制器類上應用了 Produces 特性指定了 text/json 格式,但這個方法上也應用了此特性,依據就近原則,程式會優先選用 text/plain 格式。在內部的處理機制中,這是不匹配的,除非方法的返回值型別是 string。

一旦執行,就會得到錯誤狀態碼。

 

 

下面演示一個返回 jpg 影像格式(即 image/jpeg)的例子。在剛才的 Demo 控制器類上增加一個方法,名為 GetImage。

        [Route("getpic")]
        [HttpGet]
        [Produces("image/jpeg")]
        public Stream GetImage()
        {
            // 因為應用程式目錄和內容目錄相同
            // 所以直接獲取Current即可
            string dirpath = Directory.GetCurrentDirectory();
            // 直接返回檔案流
            return System.IO.File.OpenRead(Path.Combine(dirpath, "505.jpg"));
        }

方法的返回型別為 Stream 物件,套用 image/jpeg 格式是沒問題的,畢竟影像是以二進位制的方式響應的。

老周事先從網上找了一張圖片,命名為 505.jpg,放在專案根目錄下。你在測試時可以隨便找個圖片,或者拍一張妹子的照片(前提是妹子不會報警),放到專案目錄下即可,檔名自己改。

在瀏覽器中訪問後得到結果如下圖所示。

 

 

--------------------------------------------------- 異次元分界線 -----------------------------------------------------------

既然資料可以以 JSON 格式返回,那能不能返回 XML 格式呢?當然是可以的。

    public class 帥哥
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public decimal Weight { get; set; }
    }
----------------------------------------------------------- [Route(
"getxml")] [HttpGet] [Produces("application/xml")] public 帥哥 GetXML() { return new 帥哥 { Name = "老周", Age = 93, Weight = 203.77M }; }

先是定義了一個新類,叫“帥哥”,接著,GetXML 方法返回一個“帥哥”型別的例項。注意此方法應用了 Produces 特性,指定返回的資料格式為 application/xml。

Web API 控制器預設是不啟用 XML 輸出支援的,所以在 Program.cs 檔案中,在註冊MVC功能到服務容器時,需要手動開啟對XML輸出的支援。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers().AddXmlSerializerFormatters();
var app = builder.Build();

……

這樣一來,訪問 /api/demo/getxml 就能得到 XML 資料了。

 

 

好了,今天的文章就水到這裡了,下一篇我們們聊聊 FormatFilter 特性類。

相關文章