按部就班學.Net Core Web Api開發系列【3】:WebApi開發概覽

系列目錄html

按部就班學.Net Core Web Api開發系列目錄前端

 本系列涉及到的源碼下載地址:https://github.com/seabluescn/Blog_WebApigit

 

1、概述github

目前咱們已經編寫了一些Controller並經過Swagger進行了查詢和調試,本篇將討論Controller中的一些概念,包括:ajax

一、GET、POST、PUT與DELETEjson

二、Route(路由)api

三、Reques的類型瀏覽器

四、Produces安全

 

2、GET、POST、PUT與DELETE服務器

 先看一段代碼:

    [Produces("application/json")]  
    [Route("api/products")]
    public class ProductsController : Controller
    {
        [HttpGet]  
        public Product GetProduct(String code, String Name)
        {
            Console.WriteLine("GetProduct");

            var product = new Product
            {
                ProductCode = code,
                ProductName = Name
            };

            return product;
        }
        
        [HttpPost]
        public Product GetProductByPost(String code,String Name)
        {
            Console.WriteLine("GetProductByPost");

            var product = new Product
            {
                ProductCode = code,
                ProductName = Name
            };
            return product;
        }
       
        [HttpPut]
        public Product GetProductByPut(String code, String Name)
        {
            Console.WriteLine("GetProductByPut");

            var product = new Product
            {
                ProductCode = code,
                ProductName = Name
            };
            return product;
        }    
    }

這三個方法功能徹底一致,除了提交的方式不同,並且它們均可以正常的工做。那咱們在設計的時候應該如何選擇呢?

從理論角度來說,四個提交方式對應四種類型的數據操做:

GET:查詢數據

POST:新增數據

PUT:更新數據

DELETE:刪除數據

那咱們能夠徹底按照咱們的業務類型選擇提交方式,但事實上不少框架是一個POST打天下的。下面簡單分析一下之間的區別。

一、GET與POST

GET主要用於查詢,提交數據是放在URL中的,即便採用HTTPS傳輸協議,仍然能夠被偵測到,若是查詢條件須要保密,就要採用POST,另外GET對提交的數據有長度限制,不能提交太多數據;

因爲GET是用來查詢的,對與一些公開的服務,在安全策略角度來講,可能對於GET的提交不須要進行認證審查,而非GET提交就須要身份審查,此時,就要把GET和POST嚴格分開,便於統一權限處理。

二、POST與PUT

當須要以更新的形式來修改某一具體資源的時候,如何判斷用PUT仍是POST呢?理論上講很簡單,若是該更新對應的URI屢次調用的結果一致,則PUT(冪等操做),在每次更新提交相同的內容,最終的結果不一致的時候,用POST。以上只是理論,實際上瀏覽器和服務器端容器對PUT並無什麼特別待遇,區分POST和PUT除了體現一種設計規範,對系統的功能、性能都沒有實際好處,還不如POST了。

 

3、Route(路由)

 仍是先看一段代碼:

    [Route("api/products")]
    public class ProductsController : Controller
    {        
        [HttpGet]  
        public void GetProduct()
        {
            Console.WriteLine("GetProduct,My Route:api/products"); 
            return ;
        }        

        [HttpGet("{code}")]
        public void GetProductWithCode()
        {
            Console.WriteLine("GetProductWithCode,My Route:api/products/{code}");
            return;
        }

        [HttpGet("code")]
        public void GetProductByCode()
        {
            Console.WriteLine("GetProductByCode,My Route:api/products/code");
            return;
        }
    }

[Route("api/products")]表示該Conttroller的訪問路徑前綴爲api/products;

一、不帶參數的 [HttpGet]表示該方法的訪問路徑就是:api/products

二、[HttpGet("code")]表示其訪問路徑爲:api/products/code,若是有參數的話就是:api/products/code?name=aaa&...

三、[HttpGet("{code}")]:默認要求用戶必須輸入一個參數,其值將做爲路徑的一部分,好比用戶輸入caode爲123,則路徑爲:api/products/123,若是有參數的話就是:api/products/123?name=aaa&...

有趣的是,若是用戶輸入的值爲「code」就會調用第2個方法,優先匹配靜態路徑。以上代碼只是爲了演示,實際應該避免這種寫法,不要給本身挖坑。
幾點建議:
一、應該避免某個方法因爲用戶輸入的參數緣由致使調用了另外一個方法;
二、若是某個參數是必須的,那麼該方法必須提供這個參數,下面的代碼是正確的

        [HttpGet("{code}")]
        public void GetProductWithCode(string code)
        {
            Console.WriteLine("GetProductWithCode,My Route:api/products/{code}");
            return;
        }

三、條件的名稱和路徑不要採用同一個名稱,下面的代碼是不合適的,它能夠正常工做,但難以閱讀。

        [HttpGet("code")]
        public void GetProductByCode(string code)
        {
            Console.WriteLine("GetProductByCode,My Route:api/products/code");
            return;
        }
 

4、Reques的數據類型

下面討論一下客戶端提交數據相關格式的問題,先看一下Controller的代碼。

    [Produces("application/json")]
    [Route("api/newproduct")]
    public class NewProductController : Controller
    {
        [HttpPost("t1")]
        public void PostData1(string name,string code,[FromBody]Product product)
        {            
        }

        [HttpPost("t2")]
        public void PostData2(string name, string code,Product product)
        {            
        }
    }

t1和t2方法的輸入參數徹底一致,惟一的區別是t1在Pruduct對象參數以前多一個[FromBody]標記,關於FromBody的使用:

一、若是有FromBody就須要前端提供的數據爲json格式,且只能有一個FromBody標記,多個參數須要包在一個對象裏提交;

二、若是沒有FromBody就須要前臺經過鍵值對的方式提供數據。

關於前端提交數據的類型大約有如下幾種:

一、application/x-www-form-urlencoded

經過普通的表單提交的數據就是這種數據類型。

    <form id="myform" action="api/newproduct/t2" method="post" >
            Code:<input type="text" id="Code" name="Code" /><br />
            Name:<input type="text" id="Name" name="Name" /><br />
            productCode:<input type="text" id="ProductCode" name="product.ProductCode" /><br />
            productName:<input type="text" id="ProductName" name="product.ProductName" /><br />
            <input type="submit" value="Submit" /><br />       
    </form>

也能夠經過Ajax實現。  


二、multipart/form-data

若是表單內含有文件,就須要採用這種格式,表單屬性多一個:enctype="multipart/form-data"

    <form id="myform" action="api/newproduct/t2" method="post" enctype="multipart/form-data">
            Code:<input type="text" id="Code" name="Code" /><br />
            Name:<input type="text" id="Name" name="Name" /><br />
            productCode:<input type="text" id="ProductCode" name="product.ProductCode" /><br />
            productName:<input type="text" id="ProductName" name="product.ProductName" /><br />
File:<input type="file" name="files" /><br/> <input type="submit" value="Submit" /><br /> </form>

一樣能夠經過Ajax實現調用。

以上兩種提交方式須要配套api/newproduct/t2方法


三、application/json

JSON 格式支持比鍵值對複雜得多的結構化數據,並且瀏覽器和JS支持都比較好,因此比較流行。若是你要提交的對象內含其餘對象,用json就很是方便。

$("#query1").click(function (event) { 
                var datastr = "{productCode: '3333', productName: '4444',  productNumbers: 6}";
                $.ajax({
                    url: "api/newproduct/t1?name=1111",
                    type: "Post",
                    contentType: "application/json; charset=utf-8",
                    data: datastr,
                    success: function (result) {
                        alert("success");
                    },
                    error: function (error) {
                        alert("出錯:" + error.responseText);
                    }
                });
            });

後臺服務對應api/newproduct/t1方法。

 

四、text/xml

經過XML格式輸入數據,js處理xml感受不方便,就不深刻研究了。 

小結一下:
一、若是要傳遞一個複雜對象建議採用json格式;
二、若是經過表單提交數據就用application/x-www-form-urlencoded;
三、若是有文件就必需要用multipart/form-data
只要掌握了原理,具體採用什麼方式,能夠在應用的過程當中靈活肯定,不必定拘於形式。關於更詳細一些的分析,後面會再介紹。

5、Produces:輸出數據類型

 仍是先看一段代碼:

    [Produces("application/json")]  
    [Route("api/products")]
    public class ProductsController : Controller
    {        
        [HttpGet]
        public List<Product> GetProduct()
        {           
            List<Product> products = new List<Product>
            {
                new Product("111","AAA"),
                new Product("2222","BBBB")
            };

            return products;
        }         
    }

    public class Product
    {
        public string ProductCode { get; set; }
        public string ProductName { get; set; }
        public string ProductDescript { get; set; }
        public int ProductNumbers { get; set; }

        public Product(string code,string name)
        {
            ProductCode = code;
            ProductName = name;
        }
    }

首行[Produces("application/json")] 表示調用者接收的Response數據爲json格式,經過swagger或Chrome的調試功能能夠查詢到response數據以下:

[{"productCode":"111","productName":"AAA","productDescript":null,"productNumbers":0},{"productCode":"2222","productName":"BBBB","productDescript":null,"productNumbers":0}]

若是我不但願輸出的數據類型爲json,該如何處理呢?簡單的方式就是採用ContentResult,代碼以下:

    [Produces("application/json")]
    [Route("api/products")]
    public class ProductsController : Controller
    {         
        [HttpGet("version1")]
        public string GetVersion1()
        {
            return "version:1.0.1";
        }

        [HttpGet("version2")]
        public ContentResult GetVersion2()
        {
            return Content("version:1.0.1");
        }
    }

第一個方法輸出爲:"version:1.0.1"

第二個方法輸出爲:version:1.0.1

請注意其中的區別,理論上若是能夠輸出本身定義的字符串,咱們就能夠輸出任意咱們想要的類型了,好比XML,但用這種方案輸出xml比較醜陋,若是要輸出XML類型的話,能夠採用如下辦法:

一、修改Starup類的ConfigureServices方法

        public void ConfigureServices(IServiceCollection services)
        {
           // services.AddApplicationInsightsTelemetry(Configuration);
            services.AddMvc()          
            .AddXmlDataContractSerializerFormatters(); 
        }

二、去掉Controller的[Produces("application/json")]屬性;

    [Route("api/products")]
    public class ProductsController : Controller
    {
      
        [HttpGet]
        public List<Product> GetProduct()
        {
            Console.WriteLine("GetProduct,My Route:api/products");
            List<Product> products = new List<Product>
            {
                new Product("111","AAA"),
                new Product("2222","BBBB")
            };

            return products;
        } 
    }

三、準備輸出的對象增長[Serializable]的屬性

    [Serializable]
    public class Product
    {
        public string ProductCode { get; set; }
        public string ProductName { get; set; }       
    }

此時經過客戶端的AcceptType來決定輸出的類型,能夠在Swagger界面選擇Response的類型進行接口調用。

若是選擇application/xml,輸出內容以下:

<ArrayOfProduct xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/SaleService.Models"><Product><_x003C_ProductCode_x003E_k__BackingField>111</_x003C_ProductCode_x003E_k__BackingField><_x003C_ProductDescript_x003E_k__BackingField i:nil="true" /><_x003C_ProductName_x003E_k__BackingField>AAA</_x003C_ProductName_x003E_k__BackingField><_x003C_ProductNumbers_x003E_k__BackingField>0</_x003C_ProductNumbers_x003E_k__BackingField></Product><Product><_x003C_ProductCode_x003E_k__BackingField>2222</_x003C_ProductCode_x003E_k__BackingField><_x003C_ProductDescript_x003E_k__BackingField i:nil="true" /><_x003C_ProductName_x003E_k__BackingField>BBBB</_x003C_ProductName_x003E_k__BackingField><_x003C_ProductNumbers_x003E_k__BackingField>0</_x003C_ProductNumbers_x003E_k__BackingField></Product></ArrayOfProduct>  

序列化的結果不太整齊,這個問題的緣由不在這裏討論。

若是你採用Ajax調用後臺服務,須要在js代碼中指定Accept類型。

                $.ajax({
                    url: "api/products",
                    type: "GET",
                    contentType: "application/xml; charset=utf-8",
                    headers: {
                        Accept: "application/xml"
                    },           
                    success: function (result) {
                        alert("success");
                    }
                });

關於前端調用Api的知識,後面一篇博客單獨討論,請參考:按部就班學.Net Core Web Api開發系列【4】:前端訪問WebApi

相關文章
相關標籤/搜索