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
自描述信息
客户端和服务端之前传递的每一条消息都应包含足够的信息,这些信息不仅包含了资源的表述,也包含了资源表述的相关信息(格式和内容长度),甚至包含了资源相关的其他操作信息。
超媒体作为应用程序状态引擎(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
{
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
中的 UseEndpoints
、UseMvc 或 UseMvcWithDefaultRoute 定义的传统路由访问操作。
自动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]
不适用于具有特殊含义的任何复杂的内置类型,如 IFormCollection 和 CancellationToken。 绑定源推理代码将忽略这些特殊类型。[FromForm]
针对 IFormFile 和 IFormFileCollection 类型的操作参数进行推断。 该特性不针对任何简单类型或用户定义类型进行推断。[FromRoute]
针对与路由模板中的参数相匹配的任何操作参数名称进行推断。 当多个路由与一个操作参数匹配时,任何路由值都视为[FromRoute]
。[FromQuery]
针对任何其他操作参数进行推断。
FromBody 推理说明
对于简单类型(例如 string
或 int
),推断不出 [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/json
和 application/xml
。
使用 [Consumes] 属性,操作可以限制支持的请求内容类型。 将 [Consumes]
属性应用于操作或控制器,同时指定一个或多个内容类型:
[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)
在上面的代码中,CreateProduct
操作指定内容类型 application/xml
。 路由到此操作的请求必须指定 application/xml
的 Content-Type
头。 如果请求未指定 application/xml
的 Content-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 的请求。 控制器的两个操作(PostJson
和 PostForm
)都使用相同的 URL 处理 POST 请求。 如果 [Consumes]
属性不应用类型约束,则会抛出不明确匹配异常。
[Consumes]
属性应用于两个操作。 PostJson
操作处理使用 application/json
的 Content-Type
头发送的请求。 PostForm
操作处理使用 application/x-www-form-urlencoded
的 Content-Type
头发送的请求。