.NET-5..NET Core Web API
前言
参考文献
简单的Web API 应用案例
Web API身份验证token
Web API_哔哩哔哩_bilibili--github.com
- yi念之间 - 博客园 (cnblogs.com)>
Swagger/OpenAPI
参考文献
Web API 理论部分
//注意的小细节
1. 如果控制器中存在一个没有添加[HttpGet]、[HttpPost]等的public方法,Swagger就会报错,
可以用[ApiExplorerSettings(IgnoreApi = true)]
2. //给没有配置httpmethod的action添加默认操作
app.AutoHttpMethodIfActionNoBind();
3.HTTP传递参数的三种方式
- URL:适合定位;长度限制。
- QueryString:灵活;长度限制。
- 请求报文体:灵活;长度不限制;不支持GET、Delete。
3.常用的特性
[Route("api/[controller]/[action]")]
[HttpGet("{id}")] public Person GetPerson(long id);
[Produces("application/json")]//声明控制器的操作支持 application/json 的响应内容类型
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
app.MapGet("/todos/{id}", ([FromRoute] int id) => new Todo { Id = id });
Get([FromQuery]int id) https://localhost:7082/WeatherForecast?id=1
4.返回类型
//ActionResult<T> 类型。 ASP.NET Core 自动将对象序列化为 JSON
Ok("ok");此返回类型的响应代码为 200 OK
Content("ok")
BadRequest("ok")
NotFound()//返回 404 状态NotFound错误代码。
NoContent()
[FromQuery] - Gets values from the query string.
[FromRoute] - Gets values from route data.
[FromForm] - Gets values from posted form fields.
[FromBody] - Gets values from the request body.
[FromHeader] - Gets values from HTTP headers.
https://www.cnblogs.com/gygtech/p/14677658.html
1. Web API两种风格:面向过程(RPC)、面向REST(REST)
RPC:业务驱动,自然。
REST:要求开发人员对REST原则更了解、并且有更多的设计能力
RPC:“控制器/操作方法“的形式把服务器端的代码当成方法去调用。把HTTP当成传输数据的通道,不关心HTTP谓词。通过QueryString、请求报文体给服务器传递数据。状态码。比如:/Persons/GetAll、/Persons/GetById?id=8、/Persons/Update、/Persons/DeleteById/8;
REST:按照HTTP的语义来使用HTTP协议:
1、URL用于资源的定位:/user/888、/user/888/orders;
2、HTTP谓词:GET、POST(新增)、PUT(整体更新)、DELETE、PATCH(局部更新)等;
3、什么是“幂等”,举例?DELETE、PUT、GET是幂等的,POST不幂等;
4、GET的响应可以被缓存;
5、服务器端要通过状态码来反映资源获取的结果:404、403(没有权限)、201(新增成功)。
//这是rest风格
[Route("api/[controller]")]
public class PersonsController : 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);
}
//REST的优缺点
1、通过URL对资源定位,语义更清晰;
2、通过HTTP谓词表示不同的操作,接口自描述;
3、可以对GET、PUT、DELETE请求进行重试;
4、可以用GET请求做缓存;
5、通过HTTP状态码反映服务器端的处理结果,统一错误处理机制。
6、网关等可以分析请求处理结果。
//缺点
1、真实系统中的资源非常复杂,很难清晰地进行资源的划分,对技术人员的业务和技术水平要求高。
2、不是所有的操作都能简单地对应到确定的HTTP谓词中。
3、系统的进化可能会改变幂等性。
4、通过URL进行资源定位不符合中文用户的习惯。
5、HTTP状态码个数有限。
6、有些环节会篡改非200响应码的响应报文。
7、有的客户端不支持PUT、DELETE请求。
2.Swagger/OpenAPI
参考文献
OpenAPI 是一种规范。
Swagger 是一种使用 OpenAPI 规范的工具。 例如,OpenAPIGenerator 和 SwaggerUI。
Swashbuckle入门使用
1.添加NeGet包
Swashbuckle.AspNetCore
2.添加并配置 Swagger 中间件
builder.Services.AddSwaggerGen();//服务
if (app.Environment.IsDevelopment())//生成的 JSON 文档和 Swagger UI 提供服务
{
app.UseSwagger();
app.UseSwaggerUI();
}
3.拓展
项目文件 .csproj添加
<PropertyGroup>
.....
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "ToDo API",
Description = "An ASP.NET Core Web API for managing ToDo items",
TermsOfService = new Uri("https://example.com/terms"),
Contact = new OpenApiContact
{
Name = "Example Contact",
Url = new Uri("https://example.com/contact")
},
License = new OpenApiLicense
{
Name = "Example License",
Url = new Uri("https://example.com/license")
}
});
//反射用于生成与 Web API 项目相匹配的 XML 文件名
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
//在控制器上添加,会生成相应的注释和帮助文档
[Produces("application/json")]//声明控制器的操作支持 application/json 的响应内容类型
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
ii.注释
/// <summary>
/// 实验
/// </summary>
/// <param name="item"></param>
/// <returns>A newly created TodoItem</returns>
/// <remarks>
/// Sample request:
///
/// POST /Todo
/// {
/// "id": 1,
/// "name": "Item #1",
/// "isComplete": true
/// }
///
/// </remarks>
/// <response code="201">Returns the newly created item</response>
/// <response code="400">If the item is null</response>
Web API 实践部分
1. web api 尝试? 、例子
[HttpPost]
public string SaveNote(SaveNoteRequest req)
{
string filename = $"./asset/{req.Title}.txt";//建个文件夹
System.IO.File.WriteAllText(filename, req.Content);
return filenam+req.ToString()e;
}
public record SaveNoteRequest(string Title,string Content);
// 推荐用ActionResult<T>,它支持类型转换
[HttpGet]
public ActionResult<SaveNoteRequest> GetPerson(int id)
{
if (id <= 0)
return BadRequest("id必须是正数");
else if (id == 1)
return new SaveNoteRequest(1, "ccc", "ccc内容");
else if (id == 2)
return new SaveNoteRequest(2, "ddd", "ddd内容");
else
return NotFound("不存在");//自定义消息
}
//捕捉URL占位符
1、在[HttpGet]、[HttpPost]等中使用占位符,比如{schoolName},
/aaa/GetAll/
[HttpGet("aaa/{paramName}")]
2、捕捉的值会被自动赋值给Action中同名的参数;如果名字不一致,可以用[FromRoute(Name="名字")
二、 简单的Web API 应用案例
ASP.NET Core 中的 CRUD 操作
- 运行以下命令以创建 Models 文件夹
- 将以下代码添加到 Models/Pizza.cs 以定义披萨
- 运行以下命令以创建 Services 文件夹//添加数据服务
- 将以下代码添加到 Services/PizzaService.cs,
- 选择 Controllers 文件夹,并添加名为 PizzaController.cs 的新文件
//Pizza.cs
namespace ContosoPizza.Models;
public class Pizza
{
public int Id { get; set; }
public string? Name { get; set; }
public bool IsGlutenFree { get; set; }
}
//PizzaService.cs
using ContosoPizza.Models;
namespace ContosoPizza.Services;
public static class PizzaService
{
static List<Pizza> Pizzas { get; }
static int nextId = 3;
static PizzaService()
{
Pizzas = new List<Pizza>
{
new Pizza { Id = 1, Name = "Classic Italian", IsGlutenFree = false },
new Pizza { Id = 2, Name = "Veggie", IsGlutenFree = true }
};
}
public static List<Pizza> GetAll() => Pizzas;
public static Pizza? Get(int id) => Pizzas.FirstOrDefault(p => p.Id == id);
public static void Add(Pizza pizza)
{
pizza.Id = nextId++;
Pizzas.Add(pizza);
}
public static void Delete(int id)
{
var pizza = Get(id);
if(pizza is null)
return;
Pizzas.Remove(pizza);
}
public static void Update(Pizza pizza)
{
var index = Pizzas.FindIndex(p => p.Id == pizza.Id);
if(index == -1)
return;
Pizzas[index] = pizza;
}
}
//PizzaController.cs
using ContosoPizza.Models;
using ContosoPizza.Services;
using Microsoft.AspNetCore.Mvc;
namespace ContosoPizza.Controllers;
[ApiController]
[Route("[controller]")]
public class PizzaController : ControllerBase
{
public PizzaController()
{
}
// GET all action
[HttpGet]
public ActionResult<List<Pizza>> GetAll() =>
PizzaService.GetAll();
// GET by Id action
[HttpGet("{id}")]
public ActionResult<Pizza> Get(int id)
{
var pizza = PizzaService.Get(id);
if(pizza == null)
return NotFound();
return pizza;
}
// POST action
[HttpPost]
public IActionResult Create(Pizza pizza)
{
PizzaService.Add(pizza);
return CreatedAtAction(nameof(Create), new { id = pizza.Id }, pizza);
}
// PUT action
[HttpPut("{id}")]
public IActionResult Update(int id, Pizza pizza)
{
if (id != pizza.Id)
return BadRequest();
var existingPizza = PizzaService.Get(id);
if(existingPizza is null)
return NotFound();
PizzaService.Update(pizza);
return NoContent();
}
// DELETE action
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
var pizza = PizzaService.Get(id);
if (pizza is null)
return NotFound();
PizzaService.Delete(id);
return NoContent();
}
}
三、 简单参考
超重点
参考
[HttpPost]
[Consumes("application/json")]
public IActionResult PostJson(IEnumerable<int> values) =>
Ok(new { Consumes = "application/json", Values = values });
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null){return NotFound();}
return todoItem;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
ApiController 属性
[ApiController] 属性可应用于控制器类,以启用下述 API 特定的固定行为:属性路由要求
自动 HTTP 400 响应
绑定源参数推理
Multipart/form-data 请求推理
错误状态代码的问题详细信息1.操作不可通过由UseEndpoints、UseMvc或UseMvcWithDefaultRoute定义的常规路由进行访问。
2.使模型验证错误自动触发 HTTP 400 响应。
//pet.cs
public class Pet
{
public int Id { get; set; }
[Required]
public string? Breed { get; set; }
public string? Name { get; set; }
[Required]
public PetType PetType { get; set; }
}
public enum PetType
{
Dog = 0,
Cat = 1
}
//PetsController.cs
#region snippet_Inherit
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase
#endregion
{
private static readonly List<Pet> _petsInMemoryStore = new List<Pet>();
public PetsController()
{
if (_petsInMemoryStore.Count == 0)
{
_petsInMemoryStore.Add(
new Pet
{
Breed = "Collie",
Id = 1,
Name = "Fido",
PetType = PetType.Dog
});
}
}
[HttpGet]
public ActionResult<List<Pet>> GetAll() => _petsInMemoryStore;
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Pet> GetById(int id)
{
var pet = _petsInMemoryStore.FirstOrDefault(p => p.Id == id);
#region snippet_ProblemDetailsStatusCode
if (pet == null)
{
return NotFound();
}
#endregion
return pet;
}
#region snippet_400And201
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> 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);
}
#endregion
}
//--------------