Web API项目的创建

在项目模板中选择【ASP.NET Core Web API】创建项目。

可以看到,ASP.NET Core Web API项目的结构和ASP.NET Core MVC项目的结构非常类似,不同的是ASP.NET Core Web API项目没有Views文件夹,因为Web API直接返回的是结构化的数据,不需要提供展示数据的视图。项目中生成的样板代码WeatherForecastController是一个控制器类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}

控制器类WeatherForecastController继承自ControllerBase类。我们注意到,ASP.NET Core MVC项目中控制器类继承自Controller类,Controller类是ControllerBase类的子类。Controller类中包含View等和MVC中的视图等相关的代码,因此我们在编写Web API的时候,控制器类一般不需要继承自Controller类。

Web API项目中的控制器类一般需要添加[ApiController]这个Attribute。[Route(“[controller]”)]这个Attribute是用来设置路由的对/WeatherForecast路径的请求由WeatherForecast Controller来处理。

控制器的操作方法Get上添加[HttpGet]这个Attribute表示,当向/WeatherForecast路径发送GET请求的时候,由Get方法进行处理。 由于操作方法是根据[HttpGet]这个Attribute与请求的谓词(也就是GET、POST、PUT等)进行匹配的,因此其操作方法的名字并不影响执行结果。总之,控制器类上添加的[Route(“[controller]”)]及Get方法上添加的[HttpGet]决定了客户端向/WeatherForecast这个路径发送GET请求的时候,由Get方法进行处理。Get方法返回的对象会被自动进行JOSN序列化返回客户端。

Post、Put等操作方法

Web API会根据HTTP请求的谓词来匹配操作方法,因此我们可以为控制器类增加一个标注了[HttpPost]的操作方法,这个方法就可以处理Post请求了,这同样适用于[HttpPut]等。
比如我们为WeatherForecastController增加一个SaveNote方法,这个方法把用户提交的内容保存到文本文件中,方法的返回值为保持的文件名。

1
2
3
4
5
6
7
[HttpPost]
public string saveNote(SaveNoteRequest request)
{
string filename = $"{request.Title}.txt";
System.IO.File.WriteAllTextAsync(filename, request.Content);
return $"Title: {request.Title}, Content: {request.Content}";
}

SaveNote方法有一个SaveNoteRequest类的参数,SaveNoteReauest类是一个包含Title(标题)、Content(内容)两个属性的类。
运行项目,可以看到浏览器中展示的Swagger页面中已经增加了可以处理POST请求的选项。

我们修改默认生产的请求报文体的内容,把title和content属性值修改为有实际意义的值。

Restful(reptesentational state transfer,表现层状态转移)

什么是Restful

Web API开发有两种风格:面向过程(简称RPC)、面向REST的(简称REST)。
在RPC风格的Web API中,我们通过“控制器/操作方法”的形式来调用服务器端的方法,把服务器端的代码当成方法去调用。这种风格的接口可能会用POST请求处理所有的操作方法,无论是获取、新增、更新还是删除数据,这样的接口中,我们通过QueryString(查询字符串)或这请求报文体来为服务器传递数据。只要服务器端能够正常完成客户端请求的处理,服务器就会统一返回200的HTTP状态码。对于逻辑上的错误,返回的HTTP状态码也是200,只不过在响应报文体中通过不同的错误码来表示,比如“获取的用户不存在”的错误码为1,“没有权限获取这个用户”的错误码为2。

在RPC风格的接口中,当需要加载所有用户的时候,我们就向/Persons/GetAll这个路径发送GET请求;
加载id=8,/Persons/GetById?id=8
更新id=8,/Persons/Update
新增用户,/Persons/AddNew
删除id=8,/Persons/DeleteById/8
并且把新用户信息以JSON格式放到请求报文体中;
由此可见,在RPC风格的系统中,URL中包含以名词形式描述的资源(比如Persons)和以动词形式描述的动作(比如AddNew)。

与之对应,在REST风格的Web API中,接口把服务器当成资源来处理。REST风格的接口按照HTTP设计之初的语义来使用HTTP,把系统中的所用内容都抽象为资源,所有对资源的操作都是无状态的且可以通过标准的HTTP谓词来进行。

在一个Restful风格的Web API系统中,每一个控制器都是对一类资源的操作的集合,每个操作方法都被不同的HTTP谓词触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Route("api/[controller]")]
public class PesonsController : ControllerBase
{
[HttpGet]
public IEnumerable<Person> GetPersons();

[HttpGet("{id}")]
public Person GetPerson(long id);

[HttpPut("{id}")]
public void UpdatePerson(long id,Person person);

[HttpPost]
public void SavePerson(Person person);

[HttpDelete("{id}")]
public void DeletePerson(long id);
}

Restful的优缺点

REST的优点

1.通过URL对资源定位,语义更清晰;
2.通过HTTP谓语表示不同的操作,接口自描述;
3.可以对GET、PUT、DELETE请求进行重试;
4.可以用GET请求做缓存;
5.通过HTTP状态码反映服务器端的处理结果,同样错误处理机制。
6.网管等可以分析请求处理结果。

REST的缺点

1.真是系统中的资源非常复杂,很难清晰进行资源的划分,对技术人员的业务和技术水平要求高。
2.不是所有的操作多能简单地对应到确定的HTTP谓词中。
3.系统的进化可能会改变幂等性。
4.通过URL进行资源定位不符合中文用户的习惯。
5.HTTP状体码个数有限。
6.有些环节会篡改非200响应码的响应报文。
7.有的客户端不支持PUT、DELETE请求。

Resuful中如何传递参数

HTTP传递参数的三种方式

URL:适合定位;长度限制。/
QueryString:灵活;长度限制。
请求报文体:灵活;长度不限制;不支持GET、Delete。

不同语义
URL:资源定位。
QueryString:URL之外的额外数据。
请求报文体:供PUT、POST提供数据。