【ASP.NET Core】設置Web API 響應的數據格式——Produces 特性篇

開春首文,今天老周就跟各位大夥伴們聊一個很簡單的話題:怎麼設定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就是 //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 特性類。