准备工作:在此之前你需要了解关于.NET .Core的基础,前面几篇文章已经介绍:https://www.cnblogs.com/hcyesdo/p/12834345.html

首先需要明确一点的就是REST Api它不是一个标准,而是一种架构风格

什么是WebApi?

WebApi通常是指“使用HTTP协议并通过网络调用的API”,由于它使用了HTTP协议,所以需要通过URI信息来指定端点。

WebApi就是一个Web系统,通过访问URI可以与其进行信息交互。

而常用的MVC模式是主要用来构建UI的架构模式。

特点:松耦合,关注点分离、MVC不是一个完整的应用程序框架

MVC映射为API呢?

Model:它赋值处理程序数据的逻辑

View:它是程序里复制展示数据的那部分。构建API的时候,VView就是数据或资源的展示。通常使用JSON格式。

Controller,它复负责View和Model之间的交互。

需要注意的是,在配置服务的时候在core3.0以前可能写的是AddMvc,但是这个服务涉及了View视图以及TagHelper的一些功能,所以在做WebApi的时候用不到

public void ConfigureServices(IServiceCollection services)
{
//services.AddMvc(); core 3.0以前是这样写的,这个服务包括了 TageHelper等 WebApi不需要的东西,所有3.0以后可以不这样写
services.AddControllers(); }

注意配置中间件的区域管道顺序不能随意改动。

管道就是客户端通过一些指令指向服务器端,在这个过程中呢,会经过一些手动配置的中间件,比如说路由中间件、静态资源中间件等,从客户端出发到服务器端,将数据处理后,再由服务器端原路返回到客户端这样的一个过程。但是在请求的过程中也不排除中间件出现短路的情况,这样也就不会进入到第二个中间件了,而是直接返回到客户端。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

API对外合约:API消费者需要使用到三个概念

  • 资源的标识(URI)
  • HTTP方法(GET、POST)
  • 有效载荷

API对外提供统一资源接口,业界对RESTful资源命名也有规则

关于RESTful API约束

使用名词而不是动词

需求:“我想获得系统里的所有用户”

常见错误:api/getusers

分析:这里的“获取”就是一个动词,而我们的目的应该是“用户”,即用户是一个名词

正确做法:GET api/user

要体现资源的结构/关系

通过id获取单个用户应该是:api/user/{userId},而不是 api/user/users这样写就是让API具有很好的可读性和可预测性

需求案例1:

系统存在两个资源:Company(公司)、Employee(员工),现在需要获取某个公司下的所有员工

分析:应该使用HTTP GET。API在设计的时候需要体现公司与员工的一个包含关系

常见错误做法:api/employees,api/employee/{companyId} 。这两个URI都没有体现公司和员工的一个包含关系

建议做法:api/companies/{companyId}/employees

需求案例2:

需要获取某个公司下的某个员工

常见错误做法:api/employees/{employeeId}

建议做法:api/companies/{companyId}/employees/{employeeId}

自定义查询怎么命名?

需求:获取所有用户信息,并且按年龄从大到小排序

常见错误做法:api/user/orderby/age

建议做法:api/user?orderby=age (通过QueryString查询字符串,多条件使用 & 符号)

HTTP状态码

请求是否成功?如果请求失败了,谁来为此负责

2xx 开头状态码

200 - OK,表示请求成功

201 - Created,表示请求成功并创建了资源

204 - No Content,请求成功,但是不应该返回任何对象,例如删除操作

3xx 开头状态码

用于跳转。例如告诉浏览器搜索引擎,某个页面的网址已经永久改变,绝大多数的WebApi都不需要这类的状态码

4xx 开头:客户端错误

400 - Bad Request,表示API消费者发送到服务器的请求是有错误的

401 - Unauthorized,表示没有提供授权信息或者提供的授权信息有误

403 - Forbidden,表示身份认证已经通过,但是已认证的用户却无法访问请求的资源

404 - NotFound,表示请求的资源不存在

405 - Method not allowed,当尝试发送请求到资源的时候,使用了不被支持的HTTP方法

406 - Not acceptable,表示API消费者请求的表述格式并不被WebApi所支持,并且API不会提供默认的表述格式

5xx 开头状态码

500 - Internal serever error,表示服务器出现了错误,客户端无能为力,只能以后再试试

还有就是RESTful API 返回的结果不一定Json格式的

关于如何标注路由属性 uri ?

先看控制器代码:

using Microsoft.AspNetCore.Mvc;
using Routine.Api.Service;
using System;
using System.Threading.Tasks; namespace Routine.Api.Controllers
{
[ApiController] //好处:ApiController不是强制的
//1.会启用使用属性路由(Attribute Routing)
//2.自动HTTP 400响应
//3.推断参数的绑定源
//4.Multipart/form-data 请求推断
//5.错误状态代码的问题详细信息
[Route("api/companies")] //写法一
//[Route("api/[controller]")] //写法二:意思是相当于刨除了Controller后缀,获取前面的 Companies C可以是小写,如果你改名了那么你路由的uri也跟着变了(不建议这样写)
public class CompaniesController:ControllerBase
{
private readonly ICompanyRepository _companyRepository;
public CompaniesController(ICompanyRepository companyRepository)
{
_companyRepository = companyRepository ??
throw new ArgumentNullException(nameof(companyRepository));
}
[HttpGet]
//IActionResult定义了一些合约,它可以代表ActionResult返回的结果
public async Task<IActionResult> GetCompanies()
{
var companies =await _companyRepository.GetCompaniesAsync();//读取出来的是List
return Ok(companies);
} [HttpGet("{companyId}")] // Controller标注了ApiController => uri=> api/companies/{companyId}
public async Task<IActionResult> GetCompany(Guid companyId)
{
//判断该公司是否存在方法一:这种方法在处理并发请求时可能会出现错误,原因是查到之后,进行删除,进入company后也可能是404找不到了
//var exists =await _companyRepository.CompanyExistsAsync(compamyId);
//if (!exists)
//{
// //不存在应该返回404
// return NotFound();
//}
var company = await _companyRepository.GetCompanyAsync(companyId);//读取出来的是List
//方法二
if (company==null)
{
return NotFound();
}
return Ok(company);
}
}
}

为了更好的构建RESTful API 对于 uri 的设计规则也有很严格的要求。

在控制器标注 ApiController,它会自动启用路由属性

通过 [Route] 设计路由规则

比如:接口一:GetCompanies,请求的方式:GET,通过Route 去设置路由规则 [Router("api/companies")],即查询所有公司信息

[Router("api/companies")] => api/companies

接口二:GetCompany,请求方式:GET,只不过在 添加了 [HTTPGET("{companyId}")] =>api/companies/{companyId},即查询某一公司的信息

关于第二种路由写法请看注释

通过Postman工具测试一下

测试一:接口一

测试二:接口二

以上两个接口测试完毕!!!

对于ASP.NET Core 3.x以前对于 404 NotFound请求状态码输出的格式不太友好,而ASP.NET Core 3.x对于404请求状态码也做了友好的提示。

现在将接口伪造错误信息,提示 404 如图:

关于构建 RESTful API 存在的内容协商:

所谓内容协商就是这样一个过程,针对一个响应,当有多种表述格式可用时,选取最佳的一种表述格式,这些表述可以是XML,JSON,甚至是自定义的格式规则

Accept Header:负责指定输出类型

Media Type(媒体类型)

  • application/json
  • application/xml

404 Not Acceptable

输出格式:ASP.NET Core 里面对应的就是 Output Formatters 我们称为:输出类型的格式化器

也就是说如果一个API消费者,设置了Accept Header的媒体类型为Json,那么这个RESTful API也应该返回的是JSON,

但是呢如果服务器只接收XML的格式,这个时候请求的媒体类型不被服务器所接受,那么就会返回 406 这个状态码

总而言之,尽量避免不写Accept Header,避免客户端和服务器端接收和返回的类型不一致导致错误。

有输出那么就会有输入了!!!

Content-Type-Header:负责指定输入

Media Type(媒体类型)

  • application/json
  • application/xml

输出格式:ASP.NET Core里面对应的就是 Input Formatters

比如说:对于一个客户端的POST请求,即添加资源信息,那么就需要输入参数,这些参数可能是放在Body里面,那么在Body里面的这些参数可能是对象的那种格式。那么我们就需要通过 Content-Type-Header来确定Body里面的参数是什么样的类型,可能是Json也可能是Xml或者是自定义的格式,指明之后,RESTful API才能更好的对这些参数进行处理。

看Startup类代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Routine.Api.Data;
using Routine.Api.Service; namespace Routine.Api
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services)
{
//services.AddMvc(); core 3.0以前是这样写的,这个服务包括了TageHelper等 WebApi不需要的东西,所有3.0以后可以不这样写
services.AddControllers(setup =>
{
//setup.ReturnHttpNotAcceptable=false;//如果客户端默认为xml格式,服务器端为json,false就不会返回406
setup.ReturnHttpNotAcceptable = true;//如果请求的类型和服务器请求的类型不一致就返回406 //setup.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
//setup.OutputFormatters.Insert(0, new XmlDataContractSerializerOutputFormatter());
}).AddXmlDataContractSerializerFormatters(); //配置接口服务:涉及到这个服务注册的生命周期这里采用AddScoped,表示每次的Http请求
services.AddScoped<ICompanyRepository, CompanyRepository>(); //获取配置文件中的数据库字符串连接
var sqlConnection = Configuration.GetConnectionString("SqlServerConnection"); //配置上下文类DbContext,因为它本身也是一套服务
services.AddDbContext<RoutineDbContext>(options =>
{
options.UseSqlServer(sqlConnection);
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

setup.ReturnHttpNotAcceptable就是处理是在客户端与服务器端数据产生冲突时,是否要即将产生 406 的状态码。

  • true:产生
  • false:不产生

setup.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter())

分析:实际上OutputFormatters 是一个集合 ,通过Add方法添加服务器允许接受XML格式的数据功能。因为集合中默认只有Json

setup.OutputFormatters.Insert(0,new XmlDataContractSerializerOutputFormatter())

分析:实际上刚刚写的是一种方法。Insert就是指明格式顺序,默认是JSON,通过Insert设置 0 ,就是指明XML为默认接受的数据格式

实际上以上两种写法都是 ASP.NET Core 3.x以前的写法。

ASP.NET Core 3.x的实际写法:就是在AddControllers后面添加XmlDataContractSerializerOutputFormatter方法。这样不管是输入输出都已经设置好了XML的格式数据

postman接口测试:取消setup.OutputFormatters.Insert(0,new XmlDataContractSerializerOutputFormatter())的注释

默认xml:

最后,关于构建RESTFUL Api的URI规则及原理

动作 HTTP方法 请求的参数(Payload) 参数位置 URI 请求前 请求后 响应内容
查询 GET 查询参数 可含查询字符串(QueryString)

/api/companies/{companyId}

/api/companies

无修改

单个资源

多个资源的集合

创建/添加 POST 要创建的单个资源信息 Body /api/companies 新创建的单个资源
局部修改/更新 PATCH

待修改的资源

JsonPatchDocument

Body /api/companies/{companyId}

a:1

b:2

a:1

b:3

无须返回
 替换 PUT 要替换的单个资源信息 Body  /api/companies/{companyId}

a:1

b:2

a:2

b:3

无须返回
使用预定义的表示进行创建 PUT 要创建的单个资源信息 Body /api/companies/{companyId} 返回新创建的资源
移除/删除 DELETE 可含查询字符串(QueryString) /api/companies/{companyId}

a

b

a 无须返回

.NET Core 3.x构建RESTful API 待续!!!

使用 .NET Core 3.x 构建 RESTFUL Api的更多相关文章

  1. 使用ASP.NET Core 3.x 构建 RESTful API - 2. 什么是RESTful API

    1. 使用ASP.NET Core 3.x 构建 RESTful API - 1.准备工作 什么是REST REST一词最早是在2000年,由Roy Fielding在他的博士论文<Archit ...

  2. 使用ASP.NET Core 3.x 构建 RESTful API - 1.准备工作

    以前写过ASP.NET Core 2.x的REST API文章,今年再更新一下到3.0版本. 先决条件 我在B站有一个非常入门的ASP.NET Core 3.0的视频教程,如果您对ASP.NET Co ...

  3. 使用 .NET Core 3.x 构建RESTful Api(第三部分)

    关于HTTP HEAD 和 HTTP GET: 从执行性能来说,这两种其实并没有什么区别.最大的不同就是对于HTTP HEAD 来说,Api消费者请求接口数据时,如果是通过HTTP HEAD的方式去请 ...

  4. 使用ASP.NET Core 3.x 构建 RESTful API - 3.4 内容协商

    现在,当谈论起 RESTful Web API 的时候,人们总会想到 JSON.但是实际上,JSON 和 RESTful API 没有半毛钱关系,只不过 JSON 恰好是RESTful API 结果的 ...

  5. 使用ASP.NET Core 3.x 构建 RESTful API - 3.1 资源命名

    之前讲了RESTful API的统一资源接口这个约束,里面提到了资源是通过URI来进行识别的,每个资源都有自己的URI.URI里还涉及到资源的名称,而针对资源的名称却没有一个标准来进行规范,但是业界还 ...

  6. 使用ASP.NET Core 3.x 构建 RESTful API - 4.2 过滤和搜索

    向Web API传递参数 数据可以通过多种方式来传给API. Binding Source Attributes 会告诉 Model 的绑定引擎从哪里找到绑定源. 共有以下六种 Binding Sou ...

  7. 使用ASP.NET Core 3.x 构建 RESTful API - 3.2 路由和HTTP方法

    ASP.NET Core 3.x 的路由 路由机制会把一个请求的URI映射到一个Controller上面的Action,所以当你发送一个HTTP请求的时候,MVC框架会解析这个请求的URI,并尝试着把 ...

  8. 使用ASP.NET Core 3.x 构建 RESTful API - 3.3 状态码、错误/故障、ProblemDetails

    HTTP状态码 HTTP状态码会告诉API的消费者以下事情: 请求是否执行成功了 如果请求失败了,那么谁为它负责 HTTP的状态码有很多,但是Web API不一定需要支持所有的状态码.HTTP状态码一 ...

  9. 使用ASP.NET Core 3.x 构建 RESTful API - 5.1 输入验证

    说到验证,那就需要做三件事: 定义验证规则 按验证规则进行检查 报告验证的错误.在把错误报告给API消费者的时候,报告里并不包含到底是服务端还是API消费者引起的错误,这是状态码的工作.而通常响应的B ...

随机推荐

  1. 1.对Java平台的理解。“Java是解释执行”对吗

    Java本身是一种面向对象的语言,最显著的特性有两个方面,一是所谓的“书写一次,到处运行”,能够非常容易地获得跨平台能力: 另外就是垃圾收集(GC),Java通过垃圾收集器(Garbage Colle ...

  2. Go的100天之旅-02基本语法

    基本语法 Go关键字 下面是Go的25个关键字: break default func interface select case defer go map struct chan else goto ...

  3. Spring Boot使用AOP的正确姿势

    一.为什么需要面向切面编程? 面向对象编程(OOP)的好处是显而易见的,缺点也同样明显.当需要为多个不具有继承关系的对象添加一个公共的方法的时候,例如日志记录.性能监控等,如果采用面向对象编程的方法, ...

  4. .net core https 双向验证

    文章来自:https://www.cnblogs.com/axzxs2001/p/10070562.html 关于https双向认证的知识可先行google,这时矸接代码. 为了双向认证,我们首先得准 ...

  5. Python快速入门PDF高清完整版免费下载|百度云盘

    百度云盘:Python快速入门PDF高清完整版免费下载 提取码:w5y8 内容简介 这是一本Python快速入门书,基于Python 3.6编写.本书分为4部分,第一部分讲解Python的基础知识,对 ...

  6. xss原理绕过防御个人总结

    xss原理 xss产生的原因是将恶意的html脚本代码插入web页面,底层原理和sql注入一样,都是因为js和php等都是解释性语言,会将输入的当做命令执行,所以可以注入恶意代码执行我们想要的内容 x ...

  7. 想理解JVM看了这篇文章,就知道了!

    前言 ​ 本章节属于Java进阶系列,前面关于设计模式讲解完了,有兴趣的童鞋可以翻看之前的博文,后面会讲解JVM的优化,整个系列会完整的讲解整个java体系与生态相关的中间件知识.本次将对jvm有更深 ...

  8. 打开chm文件时出现“无法显示此页:确保 Web 地址 //ieframe.dll/dnserrordiagoff.htm# 正确”的解决办法

    当我们打开chm文件时遇到下面这种情况: 解决方法: 1.一般情况下无法显示网页:右键 chm文件属性里最下面有个"解除锁定",点击"解除锁定"按钮就可以了. ...

  9. Raid0,1,5,10,50

    raid0 就是把多个硬盘合并成1个逻辑盘使用,数据读写时对各硬盘同时操作,不同硬盘写入不同数据,速度快. **最少需要2块硬盘 raid1 同时对2个硬盘读写(同样的数据).强调数据的安全性.损坏一 ...

  10. 自定义bind

    Function.prototype.mybind = function (context, ...args1) { // 判断是否为函数 if (typeof this !== 'function' ...