zoukankan      html  css  js  c++  java
  • 循序渐进学.Net Core Web Api开发系列【3】:WebApi开发概览

    系列目录

    循序渐进学.Net Core Web Api开发系列目录

     本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi

    一、概述

    目前我们已经编写了一些Controller并通过Swagger进行了查询和调试,本篇将讨论Controller中的一些概念,包括:

    1、GET、POST、PUT与DELETE

    2、Route(路由)

    3、Reques的类型

    4、Produces

    二、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打天下的。下面简单分析一下之间的区别。

    1、GET与POST

    GET主要用于查询,提交数据是放在URL中的,即使采用HTTPS传输协议,仍然可以被侦测到,如果查询条件需要保密,就要采用POST,另外GET对提交的数据有长度限制,不能提交太多数据;

    由于GET是用来查询的,对与一些公开的服务,在安全策略角度来说,可能对于GET的提交不需要进行认证审查,而非GET提交就需要身份审查,此时,就要把GET和POST严格分开,便于统一权限处理。

    2、POST与PUT

    当需要以更新的形式来修改某一具体资源的时候,如何判断用PUT还是POST呢?理论上讲很简单,如果该更新对应的URI多次调用的结果一致,则PUT(幂等操作),在每次更新提交相同的内容,最终的结果不一致的时候,用POST。以上只是理论,实际上浏览器和服务器端容器对PUT并没有什么特别待遇,区分POST和PUT除了体现一种设计规范,对系统的功能、性能都没有实际好处,还不如POST了。

    三、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;

    1、不带参数的 [HttpGet]表示该方法的访问路径就是:api/products

    2、[HttpGet("code")]表示其访问路径为:api/products/code,如果有参数的话就是:api/products/code?name=aaa&...

    3、[HttpGet("{code}")]:默认要求用户必须输入一个参数,其值将作为路径的一部分,比如用户输入caode为123,则路径为:api/products/123,如果有参数的话就是:api/products/123?name=aaa&...

    有趣的是,如果用户输入的值为“code”就会调用第2个方法,优先匹配静态路径。以上代码只是为了演示,实际应该避免这种写法,不要给自己挖坑。
    几点建议:
    1、应该避免某个方法由于用户输入的参数原因导致调用了另一个方法;
    2、如果某个参数是必须的,那么该方法必须提供这个参数,下面的代码是正确的

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

    3、条件的名称和路径不要采用同一个名称,下面的代码是不合适的,它可以正常工作,但难以阅读。

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

    四、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的使用:

    1、如果有FromBody就需要前端提供的数据为json格式,且只能有一个FromBody标记,多个参数需要包在一个对象里提交;

    2、如果没有FromBody就需要前台通过键值对的方式提供数据。

    关于前端提交数据的类型大约有以下几种:

    1、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实现。  


    2、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方法


    3、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方法。

    4、text/xml

    通过XML格式输入数据,js处理xml感觉不方便,就不深入研究了。 

    小结一下:
    1、如果要传递一个复杂对象建议采用json格式;
    2、如果通过表单提交数据就用application/x-www-form-urlencoded;
    3、如果有文件就必须要用multipart/form-data
    只要掌握了原理,具体采用什么方式,可以在应用的过程中灵活确定,不一定拘于形式。关于更详细一些的分析,后面会再介绍。

    五、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类型的话,可以采用以下办法:

    1、修改Starup类的ConfigureServices方法

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

    2、去掉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;
            } 
        }

    3、准备输出的对象增加[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

      

  • 相关阅读:
    28.注解2.md
    29.Junit测试框架.md
    WCF学习笔记(2)-WCF的通讯过程
    WCF学习笔记(1)-一个完整的例子
    Sql2008事务日志已满处理
    面向对象六大原则
    计算机基础(1)-原码、反码、补码
    Spring.Net学习笔记(7)-事务
    Spring.Net学习笔记(6)-方法注入
    Spring.Net学习笔记(5)-集合注入
  • 原文地址:https://www.cnblogs.com/seabluescn/p/9223815.html
Copyright © 2011-2022 走看看