• ASP.NET Web API基础(01)---初识 ASP.NET Web API


    1.1 什么是Web API

    WebApi是一个很广泛的概念,ASP.NET Web API是一个在.NET框架上构建Web API的框架。用于轻松构建可以由多种客户端(包括浏览器和移动设备)访问的 HTTP 服务。它是一种RestFul风格的开发接口的技术,它比WebService更省流量,比WCF更简单。

    Web APIASP.NET完整框架中地位如下图,与SignalR一起同为构建Service的框架。Web API负责构建http常规服务,而SingalR主要负责的是构建实时服务,例如股票,聊天室,在线游戏等实时性要求比较高的服务。

       

    1.1.1. WebApi的功能特点

    1.1.2. WebApiMVC的区别

    1.2 第一个Web Api 程序

    VS中创建 Asp.net Web Application 项目,项目模板中选择Web API。、

    新生成的WebAPI项目和典型的MVC项目一样,包含主要的ModelsViewsControllers等文件夹和Global.asax文件

    Controllers里面有一个ValuesController,是自动生成的一个最简单的Web API Controller

    正如我们前面所说,里面引用的是System.Web.Http命名空间。

       

    默认情况下,模板自带了两个路由规则,分别对应于WebAPI和普通MVCWeb请求,默认的WebAPI路由规则如下 

    routes.MapHttpRoute(

    name: "DefaultApi",

    routeTemplate: "api/{controller}/{id}",

    defaults: new { id = RouteParameter.Optional }

    );

    可以看到,默认路由使用的固定的api作为Uri的先导,按照微软官方的说法,用于区分普通Web请求和WebService的请求路径: 

    默认的路由规则只指向了Controller,没有指向具体的Action,因为默认情况下,对于Controller中的Action的匹配是和Action的方法名相关联的:具体来说,如果使用上面的路由规则,对应下面的Controller

    public class ContactController : ApiController

    {

    static List<Contact> contacts = new List<Contact>()

    {

    new Contact{Name="张三", Id="1", Address="邯郸", Email="", Phone="43665434"},

    new Contact{Name="李四", Id="102", Address="邯郸", Email="", Phone="13454565"}

    };

       

    public IEnumerable<Contact> Get()

    {

    return contacts;

    }

       

    public Contact Get(string id)

    {

    return contacts.FirstOrDefault(n => n.Id == id);

    }

       

    public bool Post(Contact contact)

    {

    contacts.Add(contact);

    return true;

    }

       

    public bool Put(Contact contact)

    {

    var ct = contacts.FirstOrDefault(n => n.Id == contact.Id);

    if (ct != null)

    {

    ct.Name = contact.Name;

    ct.Email = contact.Email;

    ct.Phone = contact.Phone;

    ct.Address = contact.Address;

    return true;

    }

    return false;

    }

       

    public bool Delete(string id)

    {

    return contacts.Remove(contacts.FirstOrDefault(n => n.Id == id));

    }

    }

       

    则,会有下面的对应关系:

    URL

    HttpMethod

    对应的Action名

    /api/Contact

    GET

    Get()

    /apiContact/1

    GET

    Get(string id)

    /api/Contact

    POST

    Post()

    /api/Contact/1

    DELETE

    Delete(string id)

    /api/Contact

    DELETE

    Delete()

    /api/Contact

    PUT

    Put()

       

    客户端代码:

    <script src="Scripts/jquery-3.3.1.min.js"></script>

    <script>

    //查询全部

    function getAll() {

    $.ajax({

    url: "api/contact/",

    type: "get",

    success: function (result) {

    $('tbody').html("");

    $(result).each(function (index, ele) {

    var li = "<tr><td>" + ele.Id + "</td><td>" + ele.Name + "</td><td>" + ele.Phone + "</td><td>" + ele.Address +

    "</td><td><button onclick='getItem(" + ele.Id + ")'>修改</button><button onclick='deleteItem(" + ele.Id+")'>删除</button></td></tr>"

    $('tbody').append($(li));

    });

    }

    })

    }

    //删除

    function deleteItem(id) {

    $.ajax({

    url: "api/contact/" + id,

    type: "delete",

    success: function () {

    getAll();

    }

    })

    }

    //查询单个

    function getItem(id) {

    $.ajax({

    url: "api/contact/" + id,

    type: "get",

    success: function (data) {

    $('#Id').val(data.Id);

    $('#Name').val(data.Name);

    $('#Phone').val(data.Phone);

    $('#Address').val(data.Address);

    },

    fail: function (xhr, textStatus, err) {

    alert('Error: ' + err);

    }

    })

    }

       

    $(function () {

    getAll();

    //添加

    $('#addItem').click(function () {

    var id = $('#Id').val();

    var name = $('#Name').val();

    var phone = $('#Phone').val();

    var address = $('#Address').val();

    $.ajax({

    url: "api/Contact/",

    type: "post",

    dataType: "json",

    data: { "Id": id, "Name": name, "Phone": phone, "Address": address },

    success: function () {

    getAll();

    }

    })

    })

    //修改

    $('#EditItem').click(function () {

    var id = $('#Id').val();

    var name = $('#Name').val();

    var phone = $('#Phone').val();

    var address = $('#Address').val();

    $.ajax({

    url: "api/Contact/",

    type: "put",

    dataType: "json",

    data: { "Id": id, "Name": name, "Phone": phone, "Address": address },

    success: function () {

    getAll();

    }

    })

    })

    })

    </script>

    这样就实现了最基本的CRUD操作。

    问题1:我想按照用户名称(UserName)进行查询,怎么办? 

    答:第一步:在UserController类中加一个方法名称叫:GetUserByName,如下所示: 

    public UserModel GetUserByName(string userName)

    {

    return contacts.Find((m) => { return m.UserName.Equals(userName); });

    }

    第二步:在客户端index.cshtml中调用 

    如果URL是: url: "api/User/zhang",将会报错:Bad Request 

    原因是他会自动调用我们的Get(string id) 这个方法,类型转换出错 

    解决办法: 

    改变URL为: url: "api/User/?userName=zhang", 

       

    1.3 Restful 简介

    网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备......),因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现"API First"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。在此,我们将介绍RESTful API的设计细节,探讨如何设计一套合理、好用的API

    1.3.1 什么是REST

    REST全称是Representational State Transfer,中文意思是表述(通常译为表征)性状态转移。 它首次出现在2000Roy Fielding的博士论文中,Roy FieldingHTTP规范的主要编写者之一。 他在论文中提到:"我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。" 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。

    REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST

    1.3.2 Restful API 设计

    RESTful架构应该遵循统一接口原则,统一接口包含了一组受限的预定义的操作,不论什么样的资源,都是通过使用相同的接口进行资源的访问。接口应该使用标准的HTTP方法如GETPUTPOST,并遵循这些方法的语义。

    1、协议

    API与用户的通信协议,总是使用HTTPs协议。

    2、域名

    应该尽量将API部署在专用域名之下。

    例如:https://api.example.com

    如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

    https://example.org/api/

    3、版本(Versioning

    应该将API的版本号放入URL

    例如:https://api.example.com/v1/

    另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。

    4、路径(Endpoint

    路径又称"终点"endpoint),表示API的具体网址。

    RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"collection),所以API中的名词也应该使用复数。

    举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。

    https://api.example.com/v1/zoos

    https://api.example.com/v1/animals

    https://api.example.com/v1/employees

    5HTTP动词

    对于资源的具体操作类型,由HTTP动词表示。常用的HTTP动词有下面五个(括号里是对应的SQL命令)。

    • GETSELECT):从服务器取出资源(一项或多项)。
    • POSTCREATE):在服务器新建一个资源。
    • PUTUPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
    • PATCHUPDATE):在服务器更新资源(客户端提供改变的属性)。
    • DELETEDELETE):从服务器删除资源。

      还有两个不常用的HTTP动词。

    • HEAD:获取资源的元数据。
    • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

      下面是一些例子。

      GET /zoos:列出所有动物园

      POST /zoos:新建一个动物园

      GET /zoos/ID:获取某个指定动物园的信息

      PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)

      PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)

      DELETE /zoos/ID:删除某个动物园

      GET /zoos/ID/animals:列出某个指定动物园的所有动物

      DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

      6、过滤信息(Filtering

      如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

      下面是一些常见的参数。

      ?limit=10:指定返回记录的数量

      ?offset=10:指定返回记录的开始位置。

      ?page=2&per_page=100:指定第几页,以及每页的记录数。

      ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。

      ?animal_type_id=1:指定筛选条件

      参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals GET /animals?zoo_id=ID 的含义是相同的。

      7、状态码(Status Codes

      服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

      200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。

      201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。

      202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)

      204 NO CONTENT - [DELETE]:用户删除数据成功。

      400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。

      401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。

      403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。

      404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。

      406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。

      410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。

      422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。

      500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

      8、错误处理(Error handling

      如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

      {

      error: "Invalid API key"

      }

      9、返回结果

      针对不同操作,服务器向用户返回的结果应该符合以下规范。

      GET /collection:返回资源对象的列表(数组)

      GET /collection/resource:返回单个资源对象

      POST /collection:返回新生成的资源对象

      PUT /collection/resource:返回完整的资源对象

      PATCH /collection/resource:返回完整的资源对象

      DELETE /collection/resource:返回一个空文档

      10Hypermedia API

      RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

      比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。

      {"link": {

      "rel": "collection https://www.example.com/zoos",

      "href": "https://api.example.com/zoos",

      "title": "List of zoos",

      "type": "application/vnd.yourformat+json"

      }}

      上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。

      Hypermedia API的设计被称为HATEOASGithubAPI就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

      {

      "current_user_url": "https://api.github.com/user",

      "authorizations_url": "https://api.github.com/authorizations",

      // ...

      }

      从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

      {

      "message": "Requires authentication",

      "documentation_url": "https://developer.github.com/v3"

      }

      上面代码表示,服务器给出了提示信息,以及文档的网址。

      11、其他

      1API的身份认证应该使用OAuth 2.0框架。

      2)服务器返回的数据格式,应该尽量使用JSON,避免使用XML

    问题1:什么是RestFul风格的接口?

    (1).调用者不再根据方法名称区分请求方法,而是通过请求方式进行区分,将所有的操作都抽象成对资源的增删改查。

    即:新增用Post请求,查询用get请求,修改用put请求,删除用delete请求。

    (2).请求的路径中将不在出现方法名称

    (3).用Http的返回状态码表示服务器端的处理结果,eg:找不到用404、没有权限返回201,不在需要自己约定状态码。

    (4).有利于系统优化,浏览器可以自动缓存Get请求。

    问题2:RestFul风格的弊端

    理论性太强,很容易把初学者搞晕,比如 如何通过状态码区分是"账号错误"还是"密码错误"? 比如"登录"属于什么操作?再比如 一个控制器里有两个方法 GetM1() 和 GetM2(), 两个方法中都没有参数值,这种情况如何通过请求方式来区分呢?再比如 下面的GetStudets(string str) 和 GetTeachers(string str) 方法,参数名都是str,所以无法区分(可以把其中一个参数名改为str2,就不冲突了,或者通过别的路由规则声明一下,或者给标注个非Get请求的标记,比如[HttpPost]总之很麻烦)

    1.4 生成API 帮助文档

    1.4.1 生成API文档

    通过观察,发现WebApi项目中Area文件夹下有一个HelpPage文件夹,如下图,该文件夹就是WebApi自带的生成Api的方式,如果该文件夹没了,可以通过Nuget安装:Microsoft.AspNet.WebApi.HelpPage ,你就会发现下图这一坨代码又回来了。

       

    使用:http://localhost:2131/Help/Index , 即可访问生成的Api目录,如下图:

       

    你会发现方法名的注释和参数的注释均不显示,这对使用者而言,相当不放方便了。

    改进支持参数的注释

     (1). 选中项目,右键属性,填写生成xml文件的路径,如下图: binapi.xml

    (2). 找到 Areas/HelpPage/App_Start  目录下的HelpPageConfig.cs 文件,Register 方法,添加一行代码:

    config.SetDocumentationProvider(new XmlDocumentationProvider(AppDomain.CurrentDomain.BaseDirectory + "bin\api.xml"));

    (3).再次访问 http://localhost:2131/Help/Index 发现无论是方法名还是参数名,均有描述了

       

       

       

    上述通过改进,已经生成比较完善的Api文档了,但美中不足的是不能直接测试,当然可以整合别的控件使其支持,但比较麻烦,不如使用下面的SwashBuckle生成SwaggerUI形式的Api文档。  

       

    1.4.2 借助SwaggerUI生成API文档

    1. 通过Nuget安装 Swashbuckle (版本:5.6.0)程序集,会发现在 App_Start 文件夹下生成一个 SwaggerConfig.cs 配置文件,用于配置  SwaggerUI 相关展示行为的,如下图:

     2. 选中项目,右键属性,勾上xml文档文件,注意:这里默认是什么就保留什么,不要在自己改了 。如下图: bin5-WebApiExtend.xml

       

    3. SwaggerConfig.cs文件中 搜索 c.IncludeXmlComments(GetXmlCommentsPath()); ,在这句话的下面新增一句代码:

    c.IncludeXmlComments(AppDomain.CurrentDomain.BaseDirectory + "bin\05-WebApiExtend.xml");

    4. 访问:http://localhost:2182/swagger/ui/index  ,即可查看生成Api文档。

       

       

  • 相关阅读:
    Resource和Autowired区别
    mybatisplus 分页查询+ dao层抽象
    Error attempting to get column from result set
    第一模块经济学核心原理,第一模块经济学核心原理
    springboot 优雅的启动类
    maven把依赖打进jar包
    第一章:第1课 经济学世界观(上)
    AutomicBoolean
    java异步转同步
    接口作为方法的返回值
  • 原文地址:https://www.cnblogs.com/mrfang/p/13886372.html
Copyright © 2020-2023  润新知