restful 与 webapi 详解

restful

什么是API

API全称Aplication Programming Itererface即应用程序编程接口, 我们在开发应用程序时经常用到。API作为接口,用来“连接”两个不同的系统,并使其中一方为另一 方提供服务,比如在操作系统上运行的应用程序能够访问操作系统所提供的API,并通过这些API来调用操,作系统的各种功能。因此,API 是一个系统向外暴露或公开的一套接口, 通过这些接口,外部应用程序能够访问该系统。在Web应用程序中,Web API具有同样的特性,它作为一个Web应用程序,向外提供了,在Web应用程序中,Web API具有同样的特性,它作为一个Web应用程序,向外提供了一些接口, 这些接口的功能通常是对数据进行操作,一些接口, 这些接口的功能通常是对数据进行操作(如获取或修改数据等),它们能够被外部应用程序,比如桌面应用程序、手机应用甚至其他Web应用程序(如ASP.NET Core MVC视图应用、单页Web应用)等访问并调用。WebAPI能够实现不同应用程序之间的访问,它与平台或编程语言无关,可以使用不同的技术来构建Web API,如Java、.NET等;

什么是REST

REST(Representational State Transfer) 表述性传递状态,Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格,作为一种web服务的设计与开发方式,Rest可以降低开发的复杂性,提高系统的可伸缩性。

REST是一种基于资源的架构风格,在REST中,资源(Resource)是最基本的概念任何能够命名的对象都是资源,如:user,team,order,docment。他表示web服务要操作的一个实体,一个资源具有一个统一资源标识符。(Uniform Resource Identifier URI),如 users/123。通过资源能够标识并访问资源。

资源标识

主键编号进行标识

资源集合

除了单个资源外,资源集合表示多个相同类型的资源,如users。在系统设计时,不同的实体之间往往存在着某种关联关系。如一个用户有多个订单。同样,在REST中,这种关联关系也能够有资源之间的层次关系体现出来。如users/123/orders/1

由于REST资源为中心,因此REST接口的端点(Endpoint)均以资源或资源集合结尾,它不像其他形式的WEB服务一样以动词结尾,如api/GetUserInfo或者api/UpdateUserInfo。在REST中,对资源的动作或操作是通过HTTP方法来完成的。如下

GET http://api . domain. com/users/1234 查询

PUT http://api . domain. com/users/1234 修改

上例中用到了两个HTTP方法,分别为GET与PUT,它们的作用分别是获取和更新指定资源,当请求方发起请求,修改了资源的状态后,更新后的资源表述应返回给请求方,这也是表述性状态传递的意义。

同时我们可以知道,REST是和HTTP有一定的关系,资源在服务的提供方和请求方之间进行传递。需要借助于协议来约定,比如协议所规定的消息格式等,而HTTP协议则是非常成熟且广泛使用的网络协议。事实上,HTTP协议完全满足REST中所定义的约束。因此,REST可以轻松的使用HTTP协议及其中的功能(如TTP方法,HTTP消息等),并设计出松耦合的Web服务

REST约束

客户端-服务端

​ 客户端-服务端约束体现了关注点分离原则,使客户端与服务端各自能够独立实现并独立开发,只要他们之间的接口不改变即可,客户端可以使用不同的技术或编程语言。

统一接口

​ 统一接口是设计任何RESTful服务的基础,也是区别Rest风格与其他web服务风格的最主要体现,系统中多个组件(服务器,客户端,以及可能存在的代理服务器等)都依赖统一接口,分别由4个子约束组成

资源标识

​ users/123,orders/12,docment/12

​ 通过表述操作资源

​ 格式通常为json,xml,html等,最好是

​ json {user:{"id":123,"name":Tom}} xml 123Tom

自描述信息

​ 客户端和服务端之前传递的每一条消息都应包含足够的信息,这些信息不仅包含了资源的表述,也包含了资源表述的相关信息(格式和内容长度),甚至包含了资源相关的其他操作信息。

​ 超媒体作为应用程序状态引擎(HATEOAS)

​ 服务器返回的资源表述中不仅要包含资源的表述,也应包含与之相关的链接,这些链接能够对资源执行其他操作。比如当获取资源时,返回的链接中,包含更新该资源,删除资源等链接

分层系统

​ 分层系统约束能够使用网络中介(如代理和网关)透明地部署到客户端和服务端之间,只能能够遵守并且使用前面提到的统一接口约束即可,客户端和服务端都不知道网络中介的存在。中间服务器主要用于增强安全,负载均衡和响应缓存等目的

缓存

缓存是web架构中重要的特性之一,客户端和网络中介均能够缓存服务器返回的响应,因此当服务器返回响应时,应指明该响应的缓存特性,对响应进行缓存将有助于减少数据获取延迟以及对服务器的请求,从而提高系统的性能。

无状态

​ 无状态约束将指明服务器不会记录或存储客户端的状态信息,反之,这些状态信息应有客户端来保存并维护,因此客户端对服务的请求不能依赖已发生的其他请求,当客户端请求服务器时,必须在请求消息中包含所有与之相关的信息(如认证信息等)

按需编码

按需编码约束允许服务器临时向客户端返回可执行的程序代码(如脚本等),返回这些代码用户为客户端提供扩展或自定义的功能,由于客户端必须理解并能够执行服务器返回的代码,因此这一约束增加了客户端和服务器之间的耦合,同时,这一约束是可选的 。

Restful api设计

对于REST及其约束,将有助于我们设计RESTful服务或Restful API,

在关注点方面 RESTful api 主要是面向资源,RPC 面向功能 (Remote Produce Call 远程过程调用)

在api端点方面,REST 的端点是名词,是资源或资源集合,而RPC的端点是动词、是方法名

在执行特点方面,REST对资源执行操作,RPC执行服务器上的方法

在返回结果方面,REST返回请求的资源,而RPC则返回调用方法的执行结果

什么是HTTP协议

超文本传输协议

1、URL 统一资源定义符

2、媒体类型

​ 也就是说的内容类型,Content Type

3、http消息

​ 也就是http之间交互的语言

​ 3.1 起至行

​ 3.2 HTTP消息头

​ 对于消息体的描述,和一些相关的配置

​ 3.3 空行

​ 3.4 HTTP消息体(资源,html表单)

4、http方法

​ GET POST PUT DELETE PATCH HEAD OPTIONS

​ GET 获取资源

​ POST 创建资源

​ PUT 修改资源

​ DELETE 删除资源

​ PATCH 资源部分更新 与 PUT 类似,但是是部分更新

​ HEAD 不会放回消息正文,和 GET类似

​ OPTIONS 方法用户获取资源支持的操作,

5、http消息头

​ http请求头

​ http响应头

6、状态码

​ 1XX : 信息

​ 2XX : 成功

​ 3XX :重定向

​ 4XX:客户端错误

​ 5XX:服务端错误

rest最佳实践

原则:

1、使用名词对的复数表示一个资源集合,如api.domain.com/users

2、使用斜线“/”用来表示资源之间的层次关系,如api.domain.com/users/1234/orders

3、对资源的增,删,改,查等操作名称不应包含在URL中,反之应正确使用HTTP方法,比如,GET/deleteuser/1234(错误),DELETE users/1234(正确)

4、如果一个操作无法对应到资源的某个操作上,此时可以适当地在URI中包含动词,但依然应该基于一个资源的标识符。例如:

PUT /users/1234/set-admin

DELETE /users/1234/set-admin

5、查询字符串可以用来对资源进行筛选、搜索或分页查询等操作,如下所示

GET /users?role=admin

GET /users?searchQuery=abc

GET /users?pageSize=25&pageNumber=2

6、URI 中应尽量使用小写字母

7、URI中可以使用中划线“-”来增加其可读性,如使用“-”来代替空格

http://api.domain.com/blogs/this-is-my-first-post

8、URI中不应使用下划线

​ URI中通常会带有下划线,以表示它是可点击的,这个下划线将会使URI中下划线不可见,可以使用“-”代替

9、URL末尾不应包含斜线“/”

​ 例如:/users/1234/ 错误 /users/1234 正确

restful API资源标识格式

​ JSON和XML为常用到的两种资源表述格式,他们都可以用来传递数据,且都有简洁,自描述的特点

restful api 版本

当api 发生了变化,比如资源表述内容有新增字段(字段或属性)或系统添加了新资源类型时,应使用不同的版本来区别对API的更改,为 restful api 添加版本有以下4方式

1、使用urI 路径 如 api/v1/users

2、使用查询字符串,如 api/users?version=v1

3、使用自定义消息头,如Accept-version:v1

4、使用Accept消息头,如Accept:application/json;v=2.0

webapi

就是使用asp.net core使用c#创建Restful服务,就是webapi,如果要使用webapi控制器

webapi中的控制器是派生自ControllerBase的类,

ControllerBase类

不要通过从 Controller 类派生来创建 Web API 控制器。 Controller 派生自 ControllerBase,并添加对视图的支持,因此它用于处理 Web 页面,而不是 Web API 请求。 此规则有一个例外:如果打算为视图和 Web API 使用相同的控制器,则从 Controller 派生控制器。

ControllerBase 类提供了很多用于处理 HTTP 请求的属性和方法。

例如,ControllerBase.CreatedAtAction 返回 201 状态代码:

下面是 ControllerBase 提供的方法的更多示例。

方法 说明
BadRequest 返回 400 状态代码。
NotFound 返回 404 状态代码。
PhysicalFile 返回文件。
TryUpdateModelAsync 调用模型绑定
TryValidateModel 调用模型验证

特性

Microsoft.AspNetCore.Mvc 命名空间提供可用于配置 Web API 控制器的行为和操作方法的属性。 下述示例使用属性来指定受支持的 HTTP 操作动作和所有可返回的已知 HTTP 状态代码:

[HttpPost][ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult Create(Pet pet)
{
pet.Id = _petsInMemoryStore.Any() ?
_petsInMemoryStore.Max(p => p.Id) + 1 : 1;
_petsInMemoryStore.Add(pet);
return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

特性 说明
[[Route\]] 指定控制器或操作的 URL 模式。
[[Bind\]] 指定要包含的前缀和属性,以进行模型绑定。
[[HttpGet\]] 标识支持 HTTP GET 操作谓词的操作。
[[Consumes\]] 指定某个操作接受的数据类型。
[[Produces\]] 指定某个操作返回的数据类型。

ApiController特性

特定控制器上特性

[[ApiController\]]属性可应用于控制器类,以启用下述 API 特定的固定行为:

必须有兼容性版本 2.2 或更高版本,才能使用“错误状态代码的问题详细信息” 功能。 必须有兼容性版本 2.1 或更高版本,才能使用其他功能。

多个控制器上的特性

在多个控制器上使用该属性的一种方法是创建通过 [ApiController] 属性批注的自定义基控制器类。 下述示例展示了自定义基类以及从其派生的控制器:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

程序集上特性

如果将兼容性版本设置为 2.2 或更高版本,则 [ApiController] 属性可应用于程序集。 以这种方式进行注释,会将 web API 行为应用到程序集中的所有控制器。 无法针对单个控制器执行选择退出操作。 将程序集级别的属性应用于 Startup 类两侧的命名空间声明:

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

特性路由要求

[ApiController] 属性使属性路由成为要求。 例如:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

不能通过由 Startup.Configure 中的 UseEndpointsUseMvcUseMvcWithDefaultRoute 定义的传统路由访问操作。

自动http 400响应

[ApiController] 属性使模型验证错误自动触发 HTTP 400 响应。 因此,操作方法中不需要以下代码:

if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

ASP.NET Core MVC 使用 ModelStateInvalidFilter 操作筛选器来执行上述检查。

默认 BadRequest 响应

使用 2.1 的兼容性版本时,HTTP 400 响应的默认响应类型为 SerializableError。 下述请求正文是序列化类型的示例:

JSON

{
  "": [
    "A non-empty request body is required."
  ]
}

使用 2.2 或更高版本的兼容性版本时,HTTP 400 响应的默认响应类型为 ValidationProblemDetails。 下述请求正文是序列化类型的示例:

JSON

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

ValidationProblemDetails 类型:

  • 提供计算机可读的格式来指定 Web API 响应中的错误。
  • 符合 RFC 7807 规范

记录自动 400 响应

请参阅如何对模型验证错误记录自动 400 响应 (aspnet/AspNetCore.Docs #12157)

禁用自动 400 响应

若要禁用自动 400 行为,请将 SuppressModelStateInvalidFilter 属性设置为 true。 将以下突出显示的代码添加到 Startup.ConfigureServices

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });

绑定源参数推理

绑定源特性定义可找到操作参数值的位置。 存在以下绑定源特性:

特性 绑定源
[[FromBody\]] 请求正文
[[FromForm\]] 请求正文中的表单数据
[[FromHeader\]] 请求标头
[[FromQuery\]] 请求查询字符串参数
[[FromRoute\]] 当前请求中的路由数据
[[FromServices\]] 作为操作参数插入的请求服务

如果没有 [ApiController] 属性或诸如 [FromQuery] 的绑定源属性,ASP.NET Core 运行时会尝试使用复杂对象模型绑定器。 复杂对象模型绑定器按已定义顺序从值提供程序拉取数据。

在下面的示例中,[FromQuery] 特性指示 discontinuedOnly 参数值在请求 URL 的查询字符串中提供:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

[ApiController] 属性将推理规则应用于操作参数的默认数据源。 借助这些规则,无需通过将属性应用于操作参数来手动识别绑定源。 绑定源推理规则的行为如下:

  • [FromBody] 针对复杂类型参数进行推断。 [FromBody] 不适用于具有特殊含义的任何复杂的内置类型,如 IFormCollectionCancellationToken。 绑定源推理代码将忽略这些特殊类型。
  • [FromForm] 针对 IFormFileIFormFileCollection 类型的操作参数进行推断。 该特性不针对任何简单类型或用户定义类型进行推断。
  • [FromRoute] 针对与路由模板中的参数相匹配的任何操作参数名称进行推断。 当多个路由与一个操作参数匹配时,任何路由值都视为 [FromRoute]
  • [FromQuery] 针对任何其他操作参数进行推断。

FromBody 推理说明

对于简单类型(例如 stringint),推断不出 [FromBody]。 因此,如果需要该功能,对于简单类型,应使用 [FromBody] 属性。

当操作拥有多个从请求正文中绑定的参数时,将会引发异常。 例如,以下所有操作方法签名都会导致异常:

  • [FromBody] 对两者进行推断,因为它们是复杂类型。

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • [FromBody] 对一个进行归属,对另一个进行推断,因为它是复杂类型。

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • [FromBody] 对两者进行归属。

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order o
    

禁用推理规则

若要禁用绑定源推理,请将 SuppressInferBindingSourcesForParameters 设置为 true。在 Startup.ConfigureServices 中添加下列代码:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });

Multipart/form-data 请求推理

使用 [FromForm\] 属性批注操作参数时,[ApiController] 属性应用推理规则。 将推断 multipart/form-data 请求内容类型。

要禁用默认行为,请在 Startup.ConfigureServices 中将 SuppressConsumesConstraintForFormFileParameters 属性设置为 true

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });

错误状态代码的问题详细信息

禁用 ProblemDetails 响应

SuppressMapClientErrors 属性设置为 true 时,会禁止自动创建错误状态代码的 ProblemDetails。 在 Startup.ConfigureServices 中添加下列代码:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });

使用 [Consumes] 属性定义支持的请求内容类型

默认情况下,操作支持所有可用的请求内容类型。 例如,如果应用配置为同时支持 JSON 和 XML 输入格式化程序,那么操作支持多种内容类型,其中包括 application/jsonapplication/xml

使用 [Consumes] 属性,操作可以限制支持的请求内容类型。 将 [Consumes] 属性应用于操作或控制器,同时指定一个或多个内容类型:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

在上面的代码中,CreateProduct 操作指定内容类型 application/xml。 路由到此操作的请求必须指定 application/xmlContent-Type 头。 如果请求未指定 application/xmlContent-Type 头,会生成 415 不支持的媒体类型响应。

使用 [Consumes] 属性,操作可以通过应用类型约束,根据传入请求的内容类型来影响它的选择。 请看下面的示例:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

在上面的代码中,ConsumesController 配置为处理发送到 https://localhost:5001/api/Consumes URL 的请求。 控制器的两个操作(PostJsonPostForm)都使用相同的 URL 处理 POST 请求。 如果 [Consumes] 属性不应用类型约束,则会抛出不明确匹配异常。

[Consumes] 属性应用于两个操作。 PostJson 操作处理使用 application/jsonContent-Type 头发送的请求。 PostForm 操作处理使用 application/x-www-form-urlencodedContent-Type 头发送的请求。

posted @ 2020-12-20 20:16  待我身高一米八  阅读(3425)  评论(1编辑  收藏  举报