自助网站免费注册,wordpress动漫,北京网站建设公司哪家最好,购物网站建设运营需求引言
前后端分离大致是这样的
后端#xff1a;控制层 / 业务层 / 数据操作层前端#xff1a;控制层 / 视图层
前后端的控制层#xff0c;实际上就是前后端接口的对接 前后端分离#xff0c;实现了更好地解耦合#xff0c;但也引入了接口对接的过程#xff0c;这个过程…引言
前后端分离大致是这样的
后端控制层 / 业务层 / 数据操作层前端控制层 / 视图层
前后端的控制层实际上就是前后端接口的对接 前后端分离实现了更好地解耦合但也引入了接口对接的过程这个过程常常是繁琐容易产生错误的
于是引入了api接口文档来解决这个事情如果有一份事先约定好的接口文档双方都按照这个来就能实现完美的对接。但这通常很难实现无法预先知道需要什么接口接口的参数是一个反复修改的过程
现在后端广泛采用swagger技术能够在开发时就能生成接口文档并能便捷地测试接口
对前端来说就可以根据后端项目的swagger文档来设计前端的控制层也就是通常的根目录下的api文件夹将对接口的请求封装为功能函数也是为了与视图层解耦
// user.ts
export const getUserList () request.get(/user/list)但其实一个api函数也就是对应的一个后端接口已经有了接口文档为什么不能直接生成前端的控制层
前端控制层函数看似简单其实做到类型完备函数提示清晰参数类型返回值类型各种注释是一个十分繁琐的过程
所以just relax这个过程交由swagger-typescript-api来完成吧
swagger-typescript-api
我并不是讲swagger-typescript-api教程可以去github上看它的所有用法我只是讲述一下我是如何使用它的
我的项目并不是一个大型前后端分离项目仅仅是作为练手用前后端分离的方式自己开发。如果适用于您您可以往下看
swagger-typescript-api有两种使用方式命令行 node脚本程序 优缺点显然前者方便后者易定制
我将以命令行的方式
进入我的前端项目中在shell中输入
npx swagger-typescript-api -p http://localhost:8080/v2/api-docs?groupManager -o ./src/api --axios --modular --module-name-index 1 --single-http-clienthttp://localhost:8080/v2/api-docs?groupManager 我的swagger api文档地址-o ./src/api 将生成的文件输出到src下的api目录下--axios 采用axios客户端默认fetch--modular 分离http client, data constracts, 和routes否则只会生成一个大文件 http client 这里就是axios客户端对其进行了一定的封装data constracts api接口中用到的参数或者返回值类型 --module-name-index 1 分离routes意思是按api路径.split(/)[1]拆分接口文件 比如我有两个controllerUserController和DishController访问UserController下的api都是以/admin/user开头的而访问DishController下的api是以/admin/dish开头所以这样做后也就是按照后端的controller分离api接口文件了 --single-http-client 意为只有一个http客户端稍后解释
于是在api文件夹下生成了 这里swagger-typescript-api替我生成了除API.ts外的所有文件
如果直接使用的话还是不太方便因为每个controller都是一个http客户端
意味着我需要这么调用接口 new Category().getCategoryList() new Dish().addDish() 当然最重要的是我们还需要对axios进行配置
比如添加baseUrl当然它生成的http客户端默认为localhost:8080但我们通常都会配置为环境变量以便切换不同环境下的后端比如添加请求拦截器向后端请求自动携带token认证信息比如添加响应拦截器对产生的http错误进行捕获和反馈如show error message告知unauthorized
如果没有设置--single-http-client产生的controller是这样的
class EmployeeSecurityDataType unknown extends HttpClientSecurityDataType{...}这样你需要为每个controller的http客户端进行相同的配置so dity!!!
但是设置之后产生controller是这样的
class EmployeeSecurityDataType unknown {http: HttpClientSecurityDataType;constructor(http: HttpClientSecurityDataType) {this.http http;}...
}可以看到前者是继承后者是组合也叫委派
但是我们仍然需要为每个controller委派相同的http-client所以我引入了API.ts来解决这个问题这只是一个简单的示例
class APISecurityDataType unknown extends HttpClientSecurityDataType {public category new Category(this);public common new Common(this);public dish new Dish(this);public employee new Employee(this);
}export const api new API({paramsSerializer: (params) qs.stringify(params, { indices: false }),baseURL: import.meta.env.VITE_APP_API_URL,
});api.instance.interceptors.request.use((config) {if (getToken()) {config.headers[token] getToken();}return config;},(error) {console.log(error);Promise.reject(error);}
);api.instance.interceptors.response.use((res) {const code res.data.code;const msg res.data.msg || 系统未知错误请反馈给管理员;if (res.request.responseType blob ||res.request.responseType arraybuffer) {return res;}if (code ! 1) {message.error(msg);return Promise.reject(new Error(msg));} else {return res;}},(error) {console.log(err error);let { message: msg } error;if (msg Network Error) {msg 后端接口连接异常;} else if (msg.includes(timeout)) {msg 系统接口请求超时;} else if (msg.includes(Request failed with status code)) {// 获得异常http状态码const statusCode msg.substr(msg.length - 3);if (statusCode 401) {Modal.confirm({title: 系统提示,content: 登录状态已过期请重新登录,okText: 确定,onOk() {removeToken();location.href /;},});return Promise.reject(无效的会话或者会话已过期请重新登录。);}msg 系统接口 statusCode 异常;}message.error(msg);return Promise.reject(error);}
);进行封装后我们可以更为优雅地调用api api.category.getCategoryList()
Is it elegant ?