【ASP.NET Core学习】Web API
这里介绍在asp.net core中使用web api创建 restful 服务,本文使用vscode + net core3.0
创建简单rest api
在终端输入
dotnet new webapi -n webapi
1. 创建order模型,然后初始化数据
public class orderstore { public list<order> orders { get; } = new list<order>(); public orderstore() { var random = new random(); foreach (var item in enumerable.range(1, 10)) { orders.add(new order { id = item, orderno = datetime.now.addseconds(random.next(100, 200)).addmilliseconds(random.next(20, 50)).ticks.tostring(), quantity = random.next(1, 10), amount = math.round(((decimal)random.next(100, 500) / random.next(2, 6)), 2) }); } } }
2. 简单rest api接口
/// <summary> /// 订单模块 /// </summary> [apicontroller] [route("[controller]")] [formatfilter] public class ordercontroller : controllerbase { readonly models.orderstore _orderstore = null; public ordercontroller(models.orderstore orderstore) { _orderstore = orderstore; } /// <summary> /// 查询所有订单 /// </summary> [httpget] public actionresult<list<models.order>> getall() => _orderstore.orders; /// <summary> /// 获取订单 /// </summary> /// <param name="id"></param> /// <returns></returns> [httpget("{id:int}.{format?}")] public actionresult<models.order> getbyid(int id) { var order = _orderstore.orders.firstordefault(m => m.id == id); if (order == null) { return notfound(); } return order; } /// <summary> /// 创建订单 /// </summary> /// <param name="order"></param> /// <returns>成功返回订单id,失败返回-1</returns> [httppost] public actionresult<int> create(models.order order) { if (_orderstore.orders.any(m => m.orderno == order.orderno)) { return -1; } order.id = _orderstore.orders.max(m => m.id) + 1; _orderstore.orders.add(order); return order.id; } /// <summary> /// 更新订单 /// </summary> /// <returns></returns> [httpput] public actionresult<bool> update(models.order model) { console.writeline($"orderno:{model.orderno}"); var order = _orderstore.orders.firstordefault(m => m.orderno == model.orderno); if (order == null) { return notfound(); } order.amount = model.amount; order.quantity = model.quantity; return true; } /// <summary> /// 更新订单指定信息 /// </summary> /// <remarks> /// sample request: /// /// patch /order/{orderno} /// [ /// { /// "op": "test", /// "path": "/quantity", /// "value": "2" /// }, /// { /// "op": "test", /// "path": "/amount", /// "value": "38.28" /// }, /// { /// "op": "add", /// "path": "/iscomplete", /// "value": "true" /// }, /// ] /// </remarks> /// <returns>返回是否成功</returns> /// <response code="200">提交成功</response> /// <response code="400">提交参数异常</response> /// <response code="404">订单号不存在</response> [httppatch("{orderno:length(18)}")] [producesresponsetype(statuscodes.status200ok)] [producesresponsetype(statuscodes.status404notfound)] [producesresponsetype(statuscodes.status400badrequest)] public actionresult<bool> update([frombody] jsonpatchdocument<models.order> patchdoc, [fromroute] string orderno) { var order = _orderstore.orders.firstordefault(m => m.orderno == orderno); if (order == null) { return notfound(); } patchdoc.applyto(order, modelstate); if (!modelstate.isvalid) { return badrequest(modelstate); } return ok(true); } }
3. 推荐一个vs code插件(rest client)测试接口,官方介绍
@baseurl = https://localhost:5001 ### get {{baseurl}}/order http/1.1 ### # @name order post {{baseurl}}/order http/1.1 accept: application/json content-type: application/json { "orderno": "637109312996909246", "quantity": 2, "amount": 38.28 } ### @orderid = {{order.response.body.*}} get {{baseurl}}/order/{{orderid}}.json http/1.1 ### get {{baseurl}}/order/{{orderid}}.xml http/1.1 ### get {{baseurl}}/order/{{orderid}} http/1.1 ### put {{baseurl}}/order http/1.1 content-type: application/json accept: application/json { "id": 12, "orderno": "2019112235759329", "quantity": 2, "amount": 38.28 } ### get {{baseurl}}/order/11 ### patch {{baseurl}}/order/637109312996909246 http/1.1 accept: application/json content-type: application/json [ { "op": "test", "path": "/quantity", "value": "2" }, { "op": "test", "path": "/amount", "value": "38.28" }, { "op": "add", "path": "/iscomplete", "value": "true" }, ] sample request: patch /order/{orderno} [ { "op": "test", "path": "/quantity", "value": "2" }, { "op": "test", "path": "/amount", "value": "38.28" }, { "op": "add", "path": "/iscomplete", "value": "true" }, ]
简单介绍一下,
文件后缀是http 或 rest
定义全局变量:@baseurl = https://localhost:5001 ,注意链接不加引号
### 分割多个请求
post/put 请求紧跟head请求信息,换行加上请求内容
格式化输出
services.addcontrollers(options => { options.respectbrowseracceptheader = true; //接受浏览器标头 }) .addxmlserializerformatters(); //添加xml格式化 }
2. 请求是添加标头
@orderid = {{order.response.body.*}} get {{baseurl}}/order/{{orderid}} http/1.1 accept: text/xml
若不添加标头,默认使用json格式输出
二、url格式映射
1. 添加[formatfilter]过滤器,它会检查路由中格式是否存在,并且使用相应的格式化程序输出
2. 路由规则添加{format?}
[httpget("{id:int}.{format?}")] public actionresult<models.order> getbyid(int id) { var order = _orderstore.orders.firstordefault(m => m.id == id); if (order == null) { return notfound(); } return order; }
url | 响应 |
---|---|
get {{baseurl}}/order/{{orderid}} http/1.1
|
json(若配置格式化输出) |
get {{baseurl}}/order/{{orderid}}.xml
|
xml(若配置格式化输出) |
get {{baseurl}}/order/{{orderid}}.json
|
json(若配置格式化输出) |
dotnet add package microsoft.aspnetcore.mvc.newtonsoftjson
2. 配置newtonsoft.json
public void configureservices(iservicecollection services) { services.addcontrollers() .addnewtonsoftjson(options => //添加基于newtonsoftjson格式化 { options.serializersettings.dateformathandling = newtonsoft.json.dateformathandling.microsoftdateformat; options.serializersettings.dateformatstring = "yyyy-mm-dd hh:mm:ss"; options.serializersettings.nullvaluehandling = newtonsoft.json.nullvaluehandling.ignore; }); }
put 和 patch 方法用于更新现有资源。 它们之间的区别是,put 会替换整个资源,而patch 仅指定更改。
什么是json patch?
json patch官网 里面有一句介绍的很清楚:json patch is a format for describing changes to a json document. (一种描述json的变化的格式)
什么时候需要用到json patch
- 我们返回的json很大,修改可能只是某些字段
- 对性能要求比较大的地方
- 一个大的对象,好几个地方修改,然后统一接口修改
aspnet core如何处理json patch 请求
1. 添加包支持
dotnet add package microsoft.aspnetcore.jsonpatch
2. 使用 httppatch 属性进行批注
3. 接受 jsonpatchdocument<t>,通常带有 [frombody]
4. 调用 applyto 以应用更改
假设我们现在有一个完成订单的需求
- 检查金额,数量是否有变更
- 更新iscomplete = true
下面附上代码和提交的json
控制器代码
[httppatch("{orderno:length(18)}")] [producesresponsetype(statuscodes.status200ok)] [producesresponsetype(statuscodes.status404notfound)] [producesresponsetype(statuscodes.status400badrequest)] public actionresult<bool> update([frombody] jsonpatchdocument<models.order> patchdoc, [fromroute] string orderno) { var order = _orderstore.orders.firstordefault(m => m.orderno == orderno); if (order == null) { return notfound(); } patchdoc.applyto(order, modelstate); if (!modelstate.isvalid) { return badrequest(modelstate); } return ok(true); }
失败的json(金额校验不过)
patch {{baseurl}}/order/637109312996909246 http/1.1 accept: application/json content-type: application/json [ { "op": "test", "path": "/quantity", "value": "2" }, { "op": "test", "path": "/amount", "value": "38.28" }, { "op": "add", "path": "/iscomplete", "value": "true" }, ]
会在modelstate里面列出校验不过的信息
成功的json
patch {{baseurl}}/order/637109312996909246 http/1.1 accept: application/json content-type: application/json [ { "op": "test", "path": "/quantity", "value": "2" }, { "op": "test", "path": "/amount", "value": "36.8" }, { "op": "add", "path": "/iscomplete", "value": "true" }, ]
我们用get请求重新查一下,可以看到iscomplete成功被修改了
这里只是简单介绍json patch使用,更多使用方法参考json pan官网 和
api 通常需要跟客户端,前端进行沟通,需要编写文档,这需要花费大量时间。
open api是专门解决这种问题,它为restful api定义了一个标准的、与语言无关的接口,利用工具生成文档,可以做到代码即文档(逼着开发者完善注释)
aspnet core 可以使用swashbuckle.aspnetcore或nswag 生成swagger 文档
下面介绍如何使用swashbuckle.aspnetcore
一、使用swashbuckle.aspnetcore
-
安装swashbuckle.aspnetcore包
dotnet add package swashbuckle.aspnetcore
-
添加并配置 swagger 中间件
引用命名空间:using microsoft.openapi.models;services.addsingleton<models.orderstore>(); services.addswaggergen(c => { c.swaggerdoc("v1", new openapiinfo { title = "web api doc", version = "v1" }); });
app.useswagger(); app.useswaggerui(c => { c.swaggerendpoint("/swagger/v1/swagger.json", "my api v1"); });
经过上面两步就可以使用swaggerui来查看文档和测试,浏览器打开(http://{url}/swagger)
二、添加xml注释
上面生成的swagger文档是不包含xml注释,下面介绍如何添加xml注释
-
项目文件(*.csproj)添加以下
<propertygroup><generatedocumentationfile>true</generatedocumentationfile><nowarn>$(nowarn);1591</nowarn></propertygroup>加上上面生成文档后,未注释的函数,属性会发出警告,警告代码1591,忽略警告可以添加多个,分号分割
-
addswaggergen添加下面xml支持
services.addswaggergen(c => { c.swaggerdoc("v1", new openapiinfo { title = "web api doc", version = "v1" }); var xmlfile = $"{assembly.getexecutingassembly().getname().name}.xml"; var xmlpath = path.combine(appcontext.basedirectory, xmlfile); c.includexmlcomments(xmlpath); });
-
方法添加注释
/// <summary> /// 更新订单指定信息 /// </summary> /// <remarks> /// sample request: /// /// patch /order/{orderno} /// [ /// { /// "op": "test", /// "path": "/quantity", /// "value": "2" /// }, /// { /// "op": "test", /// "path": "/amount", /// "value": "38.28" /// }, /// { /// "op": "add", /// "path": "/iscomplete", /// "value": "true" /// }, /// ] /// </remarks> /// <returns>返回是否成功</returns> /// <response code="200">提交成功</response> /// <response code="400">提交参数异常</response> /// <response code="404">订单号不存在</response>
producesresponsetype 描述返回类型
remarks 会生成请求说明
-
效果
web api 使用就介绍这些,如有错漏,希望指出。
转发请标明出处:https://www.cnblogs.com/wilsonpan/p/11945856.html
示例代码:https://github.com/wilsonpan/aspnetcoreexamples/tree/master/webapi