小範筆記: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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...