【API接口开放平台】第三期
本集前端占多数,后端较少
今日学习内容
后端
- 上线接口
- 下线接口
- 调用接口
前端
- 查看接口文档页面展示
后端
1.封装请求对象
上线接口时,我们只需要知道接口id即可。因此我们在common包中封装一个公用idRequest对象,用来接收前端传来的id,单纯需要id的方法都可以用这个对象。
@Data
public class IdRequest implements Serializable {
/**
* id
*/
private Long id;
private static final long serialVersionUID = 1L;
}
2.json转java对象(Gson)
GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Java转化为Json字符串。
引入依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
使用
Gson gson = new Gson();
// 第一个参数是Json对象,格式一般为{"userAccount":"56656","accessKey":"11111","userAvatar":"5465467"},第二个参数是字节码对象
gson.fromJson(userRequestParams, com.yupi.apiclientsdk.model.User.class);
3.项目中引入Swagger
Swagger使开发人员在设计和开发API时,能够更好地进行接口文档的编写、展示和管理,以及提供在线接口测试的能力。
引入依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
定义一个配置类,用于配置Swagger生成接口文档的相关信息
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("项目接口文档") //接口文档的标题
.version("2.0") //接口文档的版本
.description("项目接口文档") //接口文档的相关描述
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.zeng.controller")) //接口所在的包
.paths(PathSelectors.any())
.build();
return docket;
}
}
直接在浏览器输入localhost:8080/doc.html访问Swagger页面
4.@profile注解
注解的作用是指定的类或方法在特定的 Profile 环境生效。在项目运行中,包括多种环境,例如线上环境prod(product)、开发环境dev(development)、测试环境test、提测环境qa、单元测试unitest等等。
举例:该配置类只在生产环境有效
@Configuration
@Profile("prod")
public class JndiDataConfig {
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
前端
1.前端页面逻辑梳理(通用)
以查看接口文档页面设计为例,掌握页面代码中每一部分的作用,数据的传递过程。见如下注释
import { PageContainer } from '@ant-design/pro-components';
import React, { useEffect, useState } from 'react';
import {Button, Card, Descriptions, Form, message, Input, Spin, Divider} from 'antd';
import {
getInterfaceInfoByIdUsingGET,
invokeInterfaceInfoUsingPOST,
} from '@/services/yuapi-backend/interfaceInfoController';
import { useParams } from '@@/exports';
/**
* 主页
* @constructor
*/
const Index: React.FC = () => {
// 声明变量
// 定义Card组件中的loading参数,配置 loading属性为true可以控制 卡片显示加载中
const [loading, setLoading] = useState(false);
// 定义data参数用于接收 '根据接口id查出的接口信息',data类型定义为API.InterfaceInfo
const [data, setData] = useState<API.InterfaceInfo>();
// 定义invokeRes参数用于接收接口调用的返回值
const [invokeRes, setInvokeRes] = useState<any>();
// 控制卡片显示加载中
const [invokeLoading, setInvokeLoading] = useState(false);
// todo 大概是获取上级组件的带的信息
const params = useParams();
// 定义loadData方法用于根据id加载当前接口所有信息
const loadData = async () => {
// 如果接口id不存在,则直接报错,返回,不再调用之后的方法
if (!params.id) {
message.error('参数不存在');
return;
}
// 在获取到接口信息之前,将卡片设置成加载中
setLoading(true);
// 用try-catch包裹获取接口信息的方法getInterfaceInfoByIdUsingGET
try {
// 定义res变量接收方法查询到的接口信息(传入id参数查询)
const res = await getInterfaceInfoByIdUsingGET({
//此处大括号的意思表示传入一个对象,其中id的值为params.id,强转成Number类型
id: Number(params.id),
});
//由于返回值code,data,message三个东西,取出其中我们需要的data数据通过setData方法赋值给data
setData(res.data);
} catch (error: any) {
message.error('请求失败,' + error.message);
}
//数据读取到之后,显示数据,并取消 卡片'加载中'
setLoading(false);
};
//初始化数据,在页面打开时调用loadData()方法请求页面数据
useEffect(() => {
loadData();
}, []);
// 页面点击提交时执行的方法: onFinish, values为页面提交的数据
const onFinish = async (values: any) => {
// 如果接口id不存在,则直接报错,返回,不再调用之后的方法
if (!params.id) {
message.error('接口不存在');
return;
}
//将卡片设置为 '加载中'
setInvokeLoading(true);
//定义res变量接收调用接口后的返回值,传入接口id和请求参数
try {
const res = await invokeInterfaceInfoUsingPOST({
id: params.id,
...values,
});
//由于返回值code,data,message三个东西,取出其中我们需要的data数据通过setInvokeRes方法赋值给invokeRes
setInvokeRes(res.data);
message.success('请求成功');
} catch (error: any) {
message.error('操作失败,' + error.message);
}
//将卡片取消 '加载中'
setInvokeLoading(false);
};
// return 里面才是真正的页面样式代码 上面全是定义的页面中调用的方法,数据
return (
<PageContainer title="查看接口文档">
<Card>
{data ? (
<Descriptions title={data.name} column={2}>
<Descriptions.Item label="接口状态">{data.status ? '开启' : '关闭'}</Descriptions.Item>
<Descriptions.Item label="描述">{data.description}</Descriptions.Item>
<Descriptions.Item label="请求地址">{data.url}</Descriptions.Item>
<Descriptions.Item label="请求方法">{data.method}</Descriptions.Item>
<Descriptions.Item label="请求参数">{data.requestParams}</Descriptions.Item>
<Descriptions.Item label="请求头">{data.requestHeader}</Descriptions.Item>
<Descriptions.Item label="响应头">{data.responseHeader}</Descriptions.Item>
<Descriptions.Item label="创建时间">{data.createTime}</Descriptions.Item>
<Descriptions.Item label="更新时间">{data.updateTime}</Descriptions.Item>
</Descriptions>
) : (
<>接口不存在</>
)}
</Card>
<Divider />
<Card title="在线测试">
<Form name="invoke" layout="vertical" onFinish={onFinish}>
<Form.Item label="请求参数" name="userRequestParams">
<Input.TextArea />
</Form.Item>
<Form.Item wrapperCol={{ span: 16 }}>
<Button type="primary" htmlType="submit">
调用
</Button>
</Form.Item>
</Form>
</Card>
<Divider />
<Card title="返回结果" loading={invokeLoading}>
{invokeRes}
</Card>
</PageContainer>
);
};
export default Index;
2.前端页面路由显示的逻辑
在config文件夹中的routes.ts配置好路径和对应组件的关系(路由),当用户访问路径时就可以显示对应的组件
{ path: '/', name: '主页', icon: 'smile', component: './Index' },
{ path: '/interface_info/:id', name: '查看接口', icon: 'smile', component: './InterfaceInfo', hideInMenu: true },
hideInMenu属性控制是否在菜单栏显示。
3.固定值提取成常量
前端中多次重复的数字或者地址,我们把它提取成一个变量,这样就便于引用和修改。
4.... 的操作符
用来把数组和对象展开成单个数据或字段。
举例:
// 将values对象拆开成每个字段传给方法
const res = await invokeInterfaceInfoUsingPOST({
id: params.id,
...values,
});
5. Number(params.id)
是将括号中的数据强转成Number类型
6. async和awiat
async 表示这是一个async函数, await只能用在async函数里面,不能单独使用 async 返回的是一个Promise对象,await就是等待这个promise的返回结果后,再继续执行 await 等待的是一个Promise对象,后面必须跟一个Promise对象,但是不必写then(),直接就可以得到返回值
7.钩子函数
useParams()
使用它来访问当前<Route>的 id参数
useState()
函数式组件通过使用useState()可以创建自己的状态
const [state,setState] = useState(initialState),useState的返回值是一个数组,包含两个值,第一个是所定义的变量,第二个是设置变量的函数。如果useState()传入一个参数,那么这个参数作为初始状态存到state中。后续要用到状态里的数据就直接调用state变量就行了,要对状态进行修改,调用setState()函数就行。
注意:[ ]里面定义的变量名和函数名并不固定,可根据需要自己命名
【如果传入的是一个回调函数 useState((value)=>value),那么该回调函数会传入一个值,这个值永远都是这次更新前的那个状态值,因此如果该操作是异步的,那么两种方式的结果可能会有所不同。这暂不理解】
useEffect()
简单来说就是调用某个方法初始化数据,比如页面打开时初始化数据。
举例:loadData()是我们自己定义的一个方法,用于向后端请求接口信息
//初始化数据,在页面打开时调用loadData()方法请求页面数据
useEffect(() => {
loadData();
}, []);
相关文章:useEffect 依赖项数组的不同场景总结 & 一组导入操作的小技巧叶一一的技术博客51CTO博客
其它知识
1.什么是面包屑
面包屑是作为辅助和补充的导航方式,它能让用户知道在网站或应用中所处的位置并能方便地回到原先的地点。 最常见的面包屑的样式是:横向的文字链接,由大于号“>”分开,这个符号也暗示了它们的层级关系。
1.基于位置的面包屑用于告知用户在当前网站中所在的结构层级。用在具有多级导航(通常具有2级以上导航)的网站中。
2.基于产品的属性。(Attribute-based)
这种类型的面包屑常出现在具有大量类别产品和服务的网站中,如电子商务网站,网上教学服务等。
TODO
1.时序图绘制
2.vuepress搭建开发者文档
3.aop切面如何实现验证权限校验
4.用户可以申请更换签名
5.新建中的请求方法框可以改成下拉选项