小範筆記:ASP.NET Core API 基礎知識與Axios前端提交數據

来源:https://www.cnblogs.com/whuanle/archive/2019/07/04/11135299.html
-Advertisement-
Play Games

跟同事合作前後端分離項目,自己對 WebApi 的很多知識不夠全,雖說不必要學全棧,可是也要瞭解基礎知識,才能合理設計介面、API,方便與前端交接。 晚上回到宿舍後,對 WebApi 的知識查漏補缺,主要補充了 WebAPi 的一些方法、特性等如何與前端契合,如何利用工具測試 API 、Axios ...


跟同事合作前後端分離項目,自己對 WebApi 的很多知識不夠全,雖說不必要學全棧,可是也要瞭解基礎知識,才能合理設計介面、API,方便與前端交接。

晚上回到宿舍後,對 WebApi 的知識查漏補缺,主要補充了 WebAPi 的一些方法、特性等如何與前端契合,如何利用工具測試 API 、Axios 請求介面。

本文主要寫 WebApi 前端請求數據到 API 、後端返回處理結果,不涉及登錄、跨域請求、前端 UI 等。(難一點我不會了。。。看張隊的公眾號,篇篇都看不懂。。。)

前提:會一點點 VUE、會一點 Axios、會一點點 Asp.net Core。

工具:Visual Studio 2019(或者其它版本) + Visual Studio Code + Swagger +Postman

由於 Visual Studio 2019 寫 ASP.NET Core 頁面時,沒有 Vue 的智能提示,所以需要使用 VSCode 來寫前端頁面。

本文 代碼 已發佈到 GitHub https://github.com/whuanle/CZGL.IKonwWebApi

 

目錄 

一. 微軟WebApi

  1. 安裝 Swagger

二. 數據綁定與獲取

  1,預設不加

  2, [FromBody]

  3, [FromForm]

  4, [FromHeader]

  5, [FromQuery]

  6, [FromRoute]

  7, [FromService]

三. action 特性方法

  1, [Route]

  2, [Bind]

  3, [Consumes]、[Produces]

  4, [HttpGet]、[HttpPost]、[HttpDelete]、[HttpPut]

四,返回類型

  1, 查詢備忘表

  2, 返回的數據類型

  3, 直接返回基元或複雜數據類型

  4, IActionResult 類型

一. 微軟WebApi

特性綁定源
[FromBody] 請求正文
[FromForm] 請求正文中的表單數據
[FromHeader] 請求標頭
[FromQuery] 請求查詢字元串參數
[FromRoute] 當前請求中的路由數據
[FromServices] 作為操作參數插入的請求服務

來一張 Postman 的圖片:

HTTP 請求中,會攜帶很多參數,這些參數可以在前端設置,例如表單、Header、文件、Cookie、Session、Token等。

那麼,上面的表格正是用來從 HTTP 請求中獲取數據的 “方法” 或者說 “手段”。HttpContext 等對象不在本文討論範圍。

Microsoft.AspNetCore.Mvc 命名空間提供很多用於配置Web API 控制器的行為和操作方法的屬性:

特性說明
[Route] 指定控制器或操作的 URL 模式。
[Bind] 指定要包含的首碼和屬性,以進行模型綁定。
[Consumes] 指定某個操作接受的數據類型。
[Produces] 指定某個操作返回的數據類型。
[HttpGet] 標識支持 HTTP GET 方法的操作。
[HttpPost] 標識支持 HTTP POST 方法的操作。
... ... ... ... ... ...

WebApi 應用

首先創建一個 Asp.Net Core MVC 應用,然後在 Controllers 目錄添加一個 API 控制器 DefaultController.cs。(這裡不創建 WebApi 而是 創建 MVC,通過 MVC 創建 API 控制器)。

創建後預設代碼:

[Route("api/[controller]")]
[ApiController]
public class DefaultController : ControllerBase
{
}
1. 安裝 Swagger

在 Nuget 中搜索 Swashbuckle.AspNetCore,或打開 程式包管理器控制台 -> 程式包管理器控制台 ,輸入以下命令進行安裝

Install-Package Swashbuckle.AspNetCore -Version 5.0.0-rc2

打開 Startup 文件,添加引用

using Microsoft.OpenApi.Models;

在 ConfigureServices 中添加服務,雙引號文字內容隨便改。

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
            });

添加中間件

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

			// 添加下麵的內容
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            });

訪問 /swagger 可以訪問到 Swagger 的 UI 界面。

為了便於查看輸出和固定埠,打開 Progarm,cs ,修改內容

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .UseUrls("https://*:5123")
                .UseStartup<Startup>();

1562163847(1)

不要使用 IIS 托管運行。

註意:本文全部使用 [HttpPost] ;全局使用 JsonResult 作為返回類型。

二. 數據綁定與獲取

1,預設不加

直接寫 action,不使用特性

        [HttpPost("aaa")]
        public async Task<JsonResult> AAA(int? a, int? b)
        {
            if (a == null || b == null)
                return new JsonResult(new { code = 0, result = "aaaaaaaa" });
            return new JsonResult(new { code = 2000, result = a + "|" + b });
        }

打開 https://localhost:5123/swagger/index.html 查看 UI 界面 1562138960(1)

也就是說,創建一個 action ,什麼都不加,預設是 query

通過 Postman 提交數據、測試介面

1562139085(1)

對於 Query 的 action 來說, axios 的寫法

    postaaa: function () {
                    axios.post('/api/default/aaa?a=111&b=222'
                    )
                        .then(res => {
                            console.log(res.data)
                            console.log(res.data.code)
                            console.log(res.data.result)
                        })
                        .catch(err => {
                            console.error(err);
                        })
                }

在網上查找資料時,發現有人說通過 params 添加數據也可以,不過筆者測試,貌似不行。

講道理,別人可以,為啥我不行。。。

axios 代碼:

  postaaa: function () {
                    axios.post('/api/default/aaa', {
                        params: {
                            a: 123,
                            b: 234
                        }
                    }
                    )
                        .then(res => {
                            console.log(res.data)
                            console.log(res.data.code)
                            console.log(res.data.result)
                        })
                        .catch(err => {
                            console.error(err);
                        })
                }

包括下麵的,都試過了,不行。

    axios.post('/api/default/aaa', {
                            a:1234,
                            b:1122
                    }
                    
                    
    axios.post('/api/default/aaa', {
                        data:{
                            a:1234,
                            b:1122
                        }
                    }

把 [HttpPost] 改成 [HttpGet] ,則可以使用

axios.post('/api/default/aaa', {
                        params: {
                            a: 123,
                            b: 234
                        }
                    }
                    ... ...

提示:

		... ...
		.then(res => {
                            console.log(res.data)
                            console.log(res.data.code)
                            console.log(res.data.result)
                        })
                        .catch(err => {
                            console.error(err);
                        })

.then 當請求成功時觸發,請求失敗時觸發 catch 。res 是請求成功後返回的信息,res.data 是請求成功後伺服器返回的信息。即是 action 處理數據後返回的信息。

在瀏覽器,按下 F12 打開控制台,點擊 Console ,每次請求後,這裡會列印請求結果和數據。

2, [FromBody]

官方文檔解釋:請求正文。[FromBody] 針對複雜類型參數進行推斷。 [FromBody] 不適用於具有特殊含義的任何複雜的內置類型,如 IFormCollection 和 CancellationToken。 綁定源推理代碼將忽略這些特殊類型。

算了,看得一頭霧水,手動實際試試。

剛剛開始的時候,我這樣使用:

        public async Task<JsonResult> BBB([FromBody]int? a, [FromBody]int? b)

結果編譯時就報錯,提示只能使用一個 [FromBody],於是改成

        [HttpPost("bbb")]
        public async Task<JsonResult> BBB([FromBody]int? a, int? b)
        {
            if (a == null || b == null) 
                return new JsonResult(new { code = 0, result = "aaaaaaaa" });
            return new JsonResult(new { code = 2000, result = a + "|" + b });
        }

打開 Swagger UI 界面,刷新一下

1562139375(1)

從圖片中發現,只有 b,沒有 a,而且右上角有下拉框,說明瞭加 [FromBody] 是 json 上傳。

那麼說明 [FromBody] 修飾得應當是對象,而不是 欄位。

修改程式如下:

	// 增加一個類型
    public class AppJson
    {
        public int? a { get; set; }
        public int? b { get; set; }
    }
    [HttpPost("bbb")]
    public async Task<JsonResult> BBB([FromBody]AppJson ss)
    {
        if (ss.a == null || ss.b == null) 
            return new JsonResult(new { code = 0, result = "aaaaaaaa" });
        return new JsonResult(new { code = 2000, result = ss.a + "|" + ss.b });
    }

再看看微軟的文檔:[FromBody] 針對複雜類型參數進行推斷。,這下可理解了。。。

即是不應該對 int、string 等類型使用 [FromBody] ,而應該使用一個 複雜類型

而且,一個 action 中,應該只能使用一個 [FromBody] 。

打開 Swagger 界面(有修改需要刷新下界面,下麵不再贅述)。

1562139627(1)

這樣才是我們要的結果嘛,前端提交的是 Json 對象。

用 Postman 測試下

1562139749(1)

證實了猜想,嘿嘿,嘿嘿嘿。

前端提交的是 Json 對象,遵循 Json 的格式規範,那麼 [FromBody] 把它轉為 Object 對象。

前端 axios 寫法:

            methods: {
                postaaa: function () {
                    axios.post('/api/default/bbb', {
                        "a": 4444,
                        "b": 5555
                    })
                        .then(res => {
                            console.log(res.data)
                            console.log(res.data.code)
                            console.log(res.data.result)
                        })
                        .catch(err => {
                            console.error(err);
                        })
                }
            }
3, [FromForm]
        [HttpPost("ccc")]
        public async Task<JsonResult> CCC([FromForm]int? a, [FromForm]int? b)
        {
            if (a == null || b == null)
                return new JsonResult(new { code = 0, result = "aaaaaaaa" });
            return new JsonResult(new { code = 200, result = a + "|" + b });
        }

當然,這樣寫也行,多個欄位或者對象都可以

        [HttpPost("ccc")]
        public async Task<JsonResult> CCC([FromForm]AppJson ss)
        {
            if (ss.a == null || ss.b == null)
                return new JsonResult(new { code = 0, result = "aaaaaaaa" });
            return new JsonResult(new { code = 200, result = ss.a + "|" + ss.b });
        }

1562141896(1)

根據提示,使用 Postman 進行測試

0187f3234bb69a6eea144a3a16ee5d8

事實上,這樣也行 ↓

form-data 和 x-www.form-urlencoded 都是鍵值形式,文件 form-data 可以用來上傳文件。具體的區別請自行查詢。

df8a45f6c95af394ae2fdbb269f9ae2

axios 寫法(把 Content-Type 欄位修改成 form-data 或 x-www.form-urlencoded )

 postccc: function () {
                    let fromData = new FormData()
                    fromData.append('a', 111)
                    fromData.append('b', 222)
                    axios.post('/api/default/ccc', fromData, {
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded'
                        }
                    })
                        .then(res => {
                            console.log(res.data)
                            console.log(res.data.code)
                            console.log(res.data.result)
                        })
                        .catch(err => {
                            console.error(err);
                        })
                }
4, [FromHeader]

[FromHeader] 不以表單形式上傳,而是跟隨 Header 傳遞參數。

        [HttpPost("ddd")]
        public async Task<JsonResult> DDD([FromHeader]int? a, [FromHeader]int? b)
        {
            if (a == null || b == null)
                return new JsonResult(new { code = 0, result = "aaaaaaaa" });
            return new JsonResult(new { code = 200, result = a + "|" + b });
        }

1562144122(1)

axios 寫法

postddd: function () {
                    axios.post('/api/default/ddd', {}, {
                        headers: {
                            a: 123,
                            b: 133
                        }
                    })
                        .then(res => {
                            console.log(res.data)
                            console.log(res.data.code)
                            console.log(res.data.result)
                        })
                        .catch(err => {
                            console.error(err);
                        })
                }

需要註意的是,headers 的參數,必須放在第三位。沒有要提交的表單數據,第二位就使用 {} 代替。

params 跟隨 url 一起在第一位,json 或表單數據等參數放在第二位,headers 放在第三位。

由於筆者對前端不太熟,這裡有說錯,麻煩大神評論指出啦。

5, [FromQuery]

前面已經說了,Action 參數不加修飾,預設就是 [FromQuery] ,參考第一小節。

有個地方需要記住, Action 參數不加修飾。預設就是 [FromQuery] ,有時幾種參數併在一起放到 Action 里,會忽略掉,調試時忘記了,造成麻煩。

6, [FromRoute]

獲取路由規則,這個跟前端上傳的參數無關;跟 URL 可以說有關,又可以說無關。

        [HttpPost("fff")]
        public async Task<JsonResult> FFFxxx(int a,int b,
                                             [FromRoute]string controller,
                                             [FromRoute]string action)
        {
            // 這裡就不處理 a和 b了
            return new JsonResult(new { code = 200, result = controller+"|"+action });
        }

1562147096

[FromRoute] 是根據路由模板獲取的,上面 API 的兩個參數和路由模板的名稱是對應的:

[FromRoute]string controller, [FromRoute]string action
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

當然,還可以加個 [FromRoute]int? id

[FromRoute] 和 [FromQuery] 區別

以此 URL 為例

https://localhost:5123/api/Default/fff?a=111&b=22

Route 會查到 controller = Default ,action = FFFxxx 。查詢到的是代碼里的真實名稱。

Query 會查詢到 a = 111 和 b = 22

那麼,如果路由規則里,不在 URL 里出現呢?

        [HttpPost("/ooo")]
        public async Task<JsonResult> FFFooo(int a, int b,
                                             [FromRoute]string controller,
                                             [FromRoute]string action)
        {
            // 這裡就不處理 a和 b了
            return new JsonResult(new { code = 200, result = controller + "|" + action });
        }

那麼,訪問地址變成 https://localhost:5123/ooo

通過 Postman ,測試

df8a45f6c95af394ae2fdbb269f9ae2

說明瞭 [FromRoute] 獲取的是代碼里的 Controller 和 Action 名稱,跟 URL 無關,根據測試結果推斷跟路由表規則也無關。

7, [FromService]

參考 https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection?view=aspnetcore-2.2

這個是與依賴註入容器有關,跟 URL 、路由等無關。

新建一個介面、一個類

    public interface ITest
    {
        string GGG { get; }
    }
    public class Test : ITest
    {
        public string GGG { get { return DateTime.Now.ToLongDateString(); } }
    }

在 ConfigureServices 中 註入

            services.AddSingleton<ITest, Test>();

在 DefaultController 中,創建構造函數,然後

        private readonly ITest ggg;
        public DefaultController(ITest ttt)
        {
            ggg = ttt;
        }

添加一個 API

        [HttpPost("ggg")]
        public async Task<JsonResult> GGG([FromServices]ITest t)
        {
            return new JsonResult(new { code = 200, result = t.GGG });
        }

訪問時,什麼參數都不需要加,直接訪問此 API 即可。

1562148774(1)

[FromService] 跟後端的代碼有關,跟 Controller 、Action 、URL、表單數據等無關。

小結:

特性可以幾種放在一起用,不過儘量每個 API 的參數只使用一種特性。

優先取值 Form > Route > Query

IFromFile 由於文件的上傳,本文就不談這個了。

關於數據綁定,更詳細的內容請參考:

https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.2

三. action 特性方法

Microsoft.AspNetCore.Mvc 命名空間提供可用於配置 Web API 控制器的行為和操作方法的屬性。

下表是針對於 Controller 或 Action 的特性.

特性說明
[Route] 指定控制器或操作的 URL 模式。
[Bind] 指定要包含的首碼和屬性,以進行模型綁定。
[Consumes] 指定某個操作接受的數據類型。
[Produces] 指定某個操作返回的數據類型。
[HttpGet] 標識支持 HTTP GET 方法的操作。
... ...

下麵使用這些屬性來指定 Controller 或 Action 接受的 HTTP 方法、返回的數據類型或狀態代碼。

1, [Route]

在微軟文檔中,把這個特性稱為 屬性路由 ,定義:屬性路由使用一組屬性將操作直接映射到路由模板。

請教了大神,大神解釋說,ASP.NET Core 有路由規則表,路由表是全局性、唯一性的,在程式運行時,會把所有路由規則收集起來。

MVC 應用中設置路由的方法有多種,例如

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
 [Route("Home/Index")]
   public IActionResult Index()
   {
      return View();
   }
    [Route("api/[controller]")]
    [ApiController]
    public class DefaultController : ControllerBase
    {
    }

路由是全局唯一的,可以通過不同形式使用,但是規則不能發生衝突,程式會在編譯時把路由表收集起來。

根據筆者經驗,發生衝突,應該就是在編譯階段直接報錯了。(註:筆者不敢確定)

關於路由,請參考 :

https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.2#token-replacement-in-route-templates-controller-action-area

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在介紹C++淺拷貝與深拷貝之前,我們先引出C++的拷貝構造函數。 C++拷貝構造函數是一種特殊的構造函數,其形參是本類對象的引用。用於在建立一個新的對象時,使用一個已經存在的對象來初始化這個新對象。因為拷貝構造函數時特殊的構造函數,所以其沒有返回值類型,且名稱與類名相同;該函數只有一個參數,即此類對 ...
  • 9-1 餐館:創建一個名為Restaurant的類,其方法__init__()設置兩個屬性:restaurant_name和cuisine_type。創建一個名為describe_restaurant()的方法和一個名為open_restaurant()的方法,其中前者列印前述兩項消息,而後者列印一 ...
  • 反射機制初探 走進沼澤 在正常的程式中,先有類,然後再有對象。 取得Class對象(類對象) 實例觀察: 運行結果 發現:調用getClass()方法輸出的是類的完整名稱,等於找到了對象出處: ——這就是“ 反 ” Class 類對象實例化 java.lang.Class是一個類,這個類就是反射操作 ...
  • 7.4 組合 解決類與類之間代碼冗餘問題有兩種解決方案: 1、繼承:描述的是類與類之間,什麼是什麼的關係 2、組合:描述的是類與類之間的關係,是一種什麼有什麼的關係 一個類產生的對象,該對象擁有一個屬性,這個屬性的值是來自於另外一個類的對象 7.5 封裝 什麼是封裝: 裝就是把一堆屬性存起來,封的概 ...
  • 一、@函數裝飾器 裝飾器可用於修飾其他函數,例如@classmethod、@staticmethod都是函數裝飾器,他們都是Python內置的函數。 我們可以自己開發自定義函數裝飾器,例: 上面的例子可以看出,當程式使用“@函數”裝飾另一個函數時,相當於將被修飾的函數作為參數傳給@符號引用的函數,被 ...
  • 前面介紹了AWT視窗及其面板的簡單用法,其中展示出來的控制項只有按鈕一種,還有很多好用好玩的控制項有待介紹。首先是文本標簽Label,該控制項用於顯示一段平鋪文本,它不花哨也不跳動,完全就是素麵朝天的文本字元。不過,即便是文本,也能擁有鮮明的個性,猶如書法那樣,可以橫排也可以豎排,既可寫在白紙上也可寫在紅 ...
  • 該篇內容由個人博客 "點擊跳轉" 同步更新!轉載請註明出處! .NetCore徹底詮釋了“萬物皆可註入”這句話的含義,在.NetCore中到處可見註入的使用。因此core中也提供了三種註入方式的使用,分別是: 1. AddTransient:每次請求,都獲取一個新的實例。即使同一個請求獲取多次也會是 ...
  • 在實際開發過程中,很多時候會拷貝一個現有的資料庫連接字元串,修改對應的資料庫名、用戶名、密碼等配置成新的資料庫連接字元串。但是有時候我們需要增加一些額外的配置,比如超時時間,最大連接池等,此時我們可以查找資料,其實可以使用Ado.Net 的SqlConnectionStringBuilder來構造數 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...