1.1 定义

1、基础接口:单一职责原则,每个接口只负责各自的业务,下接db,通用性强。

2、聚合接口:根据调用方需求聚合基础接口数据,业务性强。

1.2 协议

1. 客户端在通过 API 与后端服务通信的过程中, 应该使用 HTTPS(生产环境) 协议

2. 服务端响应的数据格式统一为JSON

1.3域名host

prd环境:https://xxx-xxx-api.example.com/

uat环境:https://xxx-xxx-api-uat.example.com/

test环境:https://xxx-xxx-api-test.example.com/

dev环境:https://xxx-xxx-api-dev.example.com/

将api放到子域名里,这种做法可以保持某些规模化上的灵活性。

1.4路径path

path命名应该是以资源为导向的命名,对资源的操作是由HttpMethod(get、post、put、delete)来决定。所以一般来说url上的单词都应该是名词,一定不要是动词。一般遵循以下约定:

(1)URL 的命名必须全部小写;
(2) URL 必须 是易读的 URL;
(3)一定不可 暴露服务器架构

(4)出现复合词汇使用下划线分隔,例如:animal_types

举几个正面例子:

新增用户:http://localhost/user post方法提交;

修改用户:http://localhost/users put方法提交;

删除文章:http://localhost/articles?author=1&category=2
delete方法提交;

查询用户:http://localhost/users get方法提交;

查询文章:http://localhost/articles?author=1&category=2get方法提交;

错误的例子如下:

http://localhost/get_user

https://api.example.com/getUserInfo?userid=1

https://api.example.com/getusers

https://api.example.com/sv/u

https://api.example.com/cgi-bin/users/get_user.php?userid=1

1.5动词

  1. RESTful
    的核心思想就是,客户端发出的数据操作指令都是"动词 + 宾语"的结构,动词通常就是四种 HTTP 方法,对应 CRUD 操作:

GET(SELECT):从服务器取出资源(一项或多项)。

POST(CREATE):在服务器新建一个资源。

PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。

PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。

DELETE(DELETE):从服务器删除资源。

其中

(1)删除资源 必须 用 DELETE 方法

(2)创建新的资源 必须 使用 POST 方法

(3)更新资源 应该 使用 PUT 方法

(4)获取资源信息 必须 使用 GET 方法

针对每一个路径来说,下面列出所有可行的 HTTP 动词和端点的组合

请求方

URL

描述

GET

/zoos

列出所有的动物园(ID和名称,不要太详细)

POST

/zoos

新增一个新的动物园

GET

/zoos/{zoo}

获取指定动物园详情

PUT

/zoos/{zoo}

更新指定动物园(整个对象)

PATCH

/zoos/{zoo}

更新动物园(部分对象)

DELETE

/zoos/{zoo}

删除指定动物园

GET

/zoos/{zoo}/animals

检索指定动物园下的动物列表(ID和名称,不要太详

细)

GET

/animals

列出所有动物(ID和名称)。

POST

/animals

新增新的动物

GET

/animals/{animal}

获取指定的动物详情

PUT

/animals/{animal}

更新指定的动物(整个对象)

PATCH

/animals/{animal}

更新指定的动物(部分对象)

GET

/animal_types

获取所有动物类型(ID和名称,不要太详细)

GET

/animal_types/{type}

获取指定的动物类型详情

GET

/employees

检索整个雇员列表

GET

/employees/{employee}

检索指定特定的员工

GET

/zoos/{zoo}/employees

检索在这个动物园工作的雇员的名单(身份证和姓名)

POST

/employees

新增指定新员工

POST

/zoos/{zoo}/employees

在特定的动物园雇佣一名员工

DELETE

/zoos/{zoo}/employees/{employee}

从某个动物园解雇一名员工

1.6入参

1、如果记录数量很多,服务器不可能都将它们返回给用户。API 应该 提供参数,过滤返回结果。下面是一些常见的参数。

  • ?limit=10:指定返回记录的数量
  • ?offset=10:指定返回记录的开始位置。
  • ?page=2&per_page=100:指定第几页,以及每页的记录数。
  • ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
  • ?animal_type_id=1:指定筛选条件

所有URL参数 必须是全小写,必须使用下划线类型的参数形式。

分页参数 必须 固定为 page 、 per_page

经常使用的、复杂的查询 应该 标签化,降低维护成本,如

GET /trades?status=closed&sort=sortby=name&order=asc

#   可为其定制快捷方式

GET /trades/recently_closed

2、入参可分为业务参数和公共参数;公共参数有:

参数

名称

说明

timestamp

时间戳

clientid

调用方appid

统一管理应用,否则不放行

token

令牌

幂等情况可用

version

版本号

1.7响应

1、出参(返回值):必须的字段有:

字段

类型

描述

code

数值

状态码

msg

字符串

信息描述

data

结果集

返回结果集

2、如果请求处理完全正确,则状态码为0 ;

3、状态码暂定8位数数字,前4位为某一个应用(服务)拟的一个数字,后4位为具体的状态值。状态码分为2种---公共和自定义,公共码以0打头+3位数。
比如:

99990400  --客户端错误,比如请求语法格式错误、无效的请求、无效的签名等。

99991001  -----用户Id不能为空

响应的公共码如下:

编码

描述

说明

001

注解使用错误

 

002

微服务不在线,或网络超时

 

003

TOKEN解析失败

 

004

TOKEN无效或没有对应的用户

 

400

客户端错误,比如请求语法格式错误、
无效的请求、无效的签名等。

服务器 应该 放弃该请求

401

需要身份认证,比如access_token 无效/过期

客户端在收到 401 响应后,
都 应该 提示用户进行下一步的登录操作

403

没有权限访问该请求

服务器收到请求但拒绝提供服务。
如当普通用户请求操作管理员用户时,
必须 返回该状态码

404

用户请求的资源不存在

如获取不存在的用户信息

410

请求的资源不存在,并且未来也不会存在

在收到 410 状态码后,
客户端 应该 停止再次请求该资源。

429

请求次数超过允许范围

 

500

未知异常

应该 提供完整的错误信息支持,也方便跟踪调试

1.8项目结构

 

1、采用经典DDD领域取到模型:(默认一个解决方案有5个项目)

5个项目分别为:

Web层为最外层接口定义;

Service为具体的应用服务处理;

Infrastructure基础设施层,处理具体的业务逻辑和数据DB的处理;

Domain领域层为模型和仓库接口interface;

Common为通用的一些Helper类;

2、一个解决方案创建5个项目(如上图),并且里包含常用的基础组件:Log4net日志,听云监听;dockerfile,skywalking,全局异常捕捉,接口请求开始和结束的日志记录,swagger,service层的依赖注入,Mapping等。

3、代码全部采用依赖注入写法,尽量少些静态类;

4、HttpClient的写法:使用采用.netcore官方提供的方法,采用工厂类+依赖注入方式:实例代码如下:

、SartUp类里添加代码-- httpclient初始化:
services.AddHttpClient("MsgApi", c =>
{
c.BaseAddress = new Uri(Configuration["OuterApi:MsgApi:url"]);
c.Timeout = TimeSpan.FromSeconds();
}); //2 构造函注入
private IDbContext _dbContext;
private IUnitOfWork _unitOfWork;
private IordersRepository _ordersRepository;
private IordercourseRepository _ordercourseRepository;
private ILogger _logger;
privatereadonly IConfiguration _config;
privatereadonly IHttpClientFactory _clientFactory; public ordersService(IDbContext dbContext, ILogger<ordersService> logger, IConfiguration config, IHttpClientFactory clientFactory)
{
_dbContext = dbContext;
_unitOfWork = new UnitOfWork(_dbContext);
_ordersRepository = new ordersRepository(_dbContext);
_ordercourseRepository = new ordercourseRepository(_dbContext);
_mapper = mapper;
_config = config;
_logger = logger;
_clientFactory = clientFactory;
} //3使用
///<summary>
///判断此时该校区是否可以下单
///</summary>
///<param name="req"></param>
///<returns></returns>
publicasync Task<Result<string>> CheckDept(CheckSchoolDeptReq req)
{
Result<string> sendRet = new Result<string>();
try
{
HttpClient client = _clientFactory.CreateClient("ContractApi");
MyHttpClientHelper myHttpClientHelper = new MyHttpClientHelper();
MarketToUPCCheckReq checkreq = new MarketToUPCCheckReq();
sendRet = await myHttpClientHelper.GetData<Result<string>>(client, "MarketToUPCCheck", checkreq);
}
catch (Exception ex)
{
sendRet.state = false;
sendRet.error_code = ErrorCode.SysExceptionError;
sendRet.error_msg = "调用《是否可以下订单接口》报错了。请重试或者联系管理员!";
_logger.LogError(ex, ErrorCode.SysExceptionError +"调用《是否可以下订单》接口报错了:" + ex.Message);
} return sendRet;
}

1.9日志

 

1、接口开始前和结束后都已在LogstashFilter里记录,接口里就不需要再次记录;

LogstashFilter里的代码如下:

 /// <summary>
/// 记录日志用过滤器
/// </summary>
public class LogstashFilter : IActionFilter, IResultFilter
{
private string ActionArguments { get; set; } /// <summary>
/// 请求体中的所有值
/// </summary>
private string RequestBody { get; set; }
private Stopwatch Stopwatch { get; set; } private ILogger _logger; public LogstashFilter(ILogger<LogstashFilter> logger )
{
_logger = logger; } /// <summary>
/// Action 调用前执行
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{ long contentLen = context.HttpContext.Request.ContentLength == null ? : context.HttpContext.Request.ContentLength.Value;
if (contentLen > )
{
// 读取请求体中所有内容
System.IO.Stream stream = context.HttpContext.Request.Body;
if (context.HttpContext.Request.Method == "POST")
{
stream.Position = ;
}
byte[] buffer = new byte[contentLen];
stream.Read(buffer, , buffer.Length); RequestBody = System.Text.Encoding.UTF8.GetString(buffer);// 转化为字符串
} ActionArguments = JsonConvert.SerializeObject(context.ActionArguments); Stopwatch = new Stopwatch();
Stopwatch.Start(); string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
string method = context.HttpContext.Request.Method; _logger.LogInformation($"地址:{url} \n " +
$"方式:{method} \n " +
$"请求体:{RequestBody} \n " +
$"完整参数:{ActionArguments}\n " ); } /// <summary>
/// Action 方法调用后,Result 方法调用前执行
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{
// do nothing
} /// <summary>
/// Result 方法调用前(View 呈现前)执行
/// </summary>
/// <param name="context"></param>
public void OnResultExecuting(ResultExecutingContext context)
{
// do nothing
} /// <summary>
/// Result 方法调用后执行
/// </summary>
/// <param name="context"></param>
public void OnResultExecuted(ResultExecutedContext context)
{ Stopwatch.Stop();
string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
string method = context.HttpContext.Request.Method;
string qs = ActionArguments;
string res = "在返回结果前发生了异常";
if (context.Result is ObjectResult)
{
dynamic result = context.Result.GetType().Name == "EmptyResult" ? new { Value = "无返回结果" } : context.Result as dynamic;
if (result != null)
{
res = JsonConvert.SerializeObject(result.Value);
} } _logger.LogInformation($"地址:{url} \n " +
$"方式:{method} \n " +
$"请求体:{RequestBody} \n " +
$"参数:{qs}\n " +
$"结果:{res}\n " +
$"耗时:{Stopwatch.Elapsed.TotalMilliseconds} 毫秒"); }
}

2、try Catch日志必须要添加LogError日志,并且要将堆栈信息记录,代码如下:

catch (Exception ex)
{
_logger.LogError(ex, ErrorCode500 + ex.Message);
}

.netcore 3.1高性能微服务架构:webapi规范的更多相关文章

  1. .netcore 3.1高性能微服务架构:封装调用外部服务的接口方法--HttpClient客户端思路分析

    众所周知,微服务架构是由一众微服务组成,项目中调用其他微服务接口更是常见的操作.为了便于调用外部接口,我们的常用思路一般都是封装一个外部接口的客户端,使用时候直接调用相应的方法.webservice或 ...

  2. .netcore 3.1高性能微服务架构:加入swagger接口文档

    本文为原创文章:首发:http://www.zyiz.net/tech/detail-108663.html swagger是什么? Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视 ...

  3. (1)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型

    开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点: 1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块: 2)系统耦合性强,一旦其中一个模块有问题, ...

  4. (1).NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型

    开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点:1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块:2)系统耦合性强,一旦其中一个模块有问题,整个 ...

  5. 什么是微服务架构,.netCore微服务选型

    什么是微服务架构,.netCore微服务选型 https://www.cnblogs.com/uglyman/p/9182485.html 开发工具:VS2017 .Net Core 2.1 什么是微 ...

  6. 庐山真面目之八微服务架构 NetCore 基于 Dockerfile 文件部署

    庐山真面目之八微服务架构 NetCore 基于 Dockerfile 文件部署 一.简介      从今天开始,不出意外的话,以后所写的文章中所介绍项目的部署环境都应该会迁移到Linux环境上,而且是 ...

  7. 庐山真面目之九微服务架构 NetCore 基于 Docker 基础镜像和挂载文件部署

    庐山真面目之九微服务架构 NetCore 基于 Docker 基础镜像和挂载文件部署 一.简介      我们在上一篇文章<庐山真面目之八微服务架构 NetCore 基于 Dockerfile ...

  8. 腾讯开源微服务架构 Tars,高性能 RPC 开发框架

    腾讯微服务架构 Tars 于今日正式开源. Tars 取名于电影“星际穿越”中的机器人,是支持多语言的高性能 RPC 开发框架和配套一体化的服务治理平台,可以帮助企业或者用户以微服务的方式快速构建稳定 ...

  9. Java高并发高性能分布式框架从无到有微服务架构设计

    微服务架构模式(Microservice Architect Pattern).近两年在服务的疯狂增长与云计算技术的进步,让微服务架构受到重点关注 微服务架构是一种架构模式,它提倡将单一应用程序划分成 ...

随机推荐

  1. Linux 内核 MCA 总线

    微通道体系(MCA)是一个 IBM 标准, 用在 PS/2 计算机和一些笔记本电脑. 在硬件级别, 微通道比 ISA 有更多特性. 它支持多主 DMA, 32-位地址和数据线, 共享中断线, 和地理 ...

  2. JRoll 2 适用于移动开发滚动(滑动)——轻量级插件

    JRoll,一款能滚起上万条数据,具有滑动加速.回弹.缩放.滚动条.滑动事件等功能,兼容CommonJS/AMD/CMD模块规范,开源,免费的轻量级html5滚动插件. 官网:http://www.c ...

  3. js实现bind

    Function.prototype.bind=function(ctx,...lastArgs){ let self=this return (...laterArgs)=>self.appl ...

  4. 常用MouseEvent鼠标事件对象&KeyboardEvent键盘事件对象&常用键盘码

    MouseEvent鼠标事件对象: e.target //=> 事件源(操作的是哪个元素) e.clientX e.clientY //当前鼠标触发点距离当前窗口左上角的X|Y轴坐标 e.pag ...

  5. buerdepepeqi 的模版

    buerdepepeqi的模板 头文件 #include <set> #include <map> #include <deque> #include <qu ...

  6. MYSQL基本常用函数

    MYSQL基本常用函数 一.字符的操作函数 (ps:mysql中的索引都是从1开始的.) 1.instr(param1,param2) 返回子串第一次出现的索引,若找不到则返回0. param1填写操 ...

  7. MyBatis学习与使用(一)

    写在前面—— 用 MyBatis 也做过几个项目了,但是一直没有很深入的去理解这个框架,最近决定从头开始学习和整理MyBatis. 之前开发的项目并不是我先创建的,等我介入的时候发现他们已经稍稍封装了 ...

  8. 【转载】你不知道的 console,让 JS 调试更简单

    对于前端工程师,肯定不会对console陌生,但是,又能深入了解多少呢? Chrome控制台-开发者工具 windows按F12, MAC按Command + Option + C或Command + ...

  9. 【题解】HDU4689 Derangement(有技巧的计数DP)

    [题解]HDU4689 Derangement(有技巧的计数DP) 传送门 呵呵没告诉我多测组数,然后\(n\le 20,7000\mathrm{ms}\)我写了个状压上去T了 题目大意: 要你求错排 ...

  10. Asp.net Core Session 存储任意对象

    using Microsoft.AspNetCore.Http; using Newtonsoft.Json; public static class SessionExtensions { publ ...