API接口文档
1. 文档概述
产品经理的任务
产品经理定义每个版本需要实现的具体功能和细节,通常通过撰写产品需求文档来明确需求。并且撰写API接口文档告诉前后端工程师,怎样开发才能在双方完成任务后,前后端能够完美对接。
文档目的
API接口文档旨在帮助开发人员了解如何调用和使用本系统提供的API。文档包括了接口的定义、请求与响应格式、错误处理机制等内容。
系统架构
- 前端:原生JS 或 Vue.js
- 后端:Java原生 或 SpringBoot框架
API接口文档示例
部门管理
1.1 部门列表查询
1.1.1 基本信息
请求路径:/depts
请求方式:GET
接口描述:该接口用于部门列表数据查询
1.1.2 请求参数
无
1.1.3 响应数据
参数格式:application/json
参数说明:
参数名 |
类型 |
是否必须 |
备注 |
code |
number |
必须 |
响应码,1 代表成功,0 代表失败 |
msg |
string |
非必须 |
提示信息 |
data |
object[ ] |
非必须 |
返回的数据 |
\ |
- id |
number |
非必须 |
\ |
- name |
string |
非必须 |
\ |
- createTime |
string |
非必须 |
\ |
- updateTime |
string |
非必须 |
响应数据样例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
{
"code": 1,
"msg": "success",
"data": [
{
"id": 1,
"name": "学工部",
"createTime": "2022-09-01T23:06:29",
"updateTime": "2022-09-01T23:06:29"
},
{
"id": 2,
"name": "教研部",
"createTime": "2022-09-01T23:06:29",
"updateTime": "2022-09-01T23:06:29"
}
]
}
|
2. 前端使用说明
前端框架选择
1. JS原生代码(使用fetch
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
fetch('/depts', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
console.log("部门列表:", data.data);
} else {
console.log("请求失败:", data.msg);
}
})
.catch(error => {
console.error('Error:', error);
});
|
Vue.js
在Vue组件中,可以使用Axios来简化API调用。
示例代码(使用Axios发送GET请求):
错误处理
前端应对API请求中的常见错误进行处理,如404(未找到),500(服务器错误)等。
错误处理示例:
1
2
3
4
5
6
7
8
9
10
|
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch(error => {
console.error('API call failed:', error);
});
|
3. 后端实现说明
后端语言/框架选择
Java原生
使用Java原生编写API接口,通常通过HttpServlet
处理请求。
示例代码(Java原生实现GET请求):
1
2
3
4
5
6
7
8
9
10
11
|
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class DataServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.println("{\"message\":\"Hello, World!\"}");
}
}
|
SpringBoot
使用SpringBoot框架,MySQL数据库,Mybatis框架来实现后端数据的提供
示例代码(SpringBoot实现前端Get的请求):
内容是查询所有部门,要求这里是Get请求,可以使用@GetMapping
使用三层架构,DeptController,DeptService,DeptMapper,响应,处理数据,调取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//日志输出的注解
@Slf4j
//controller层必带的注解
@RestController
@RequestMapping("/dept")
public class DeptController {
Dept dp=new Dept();
//依赖注入
@Autowired
private DeptService deptService;
@GetMapping()
public Result list(){
log.info("查询所有部门数据");
List<Dept> depts= deptService.list();
return Result.success(depts);
}
}
|
这是DeptController的代码有@RequestMapping("/dept")后可以在后面定义类似GetMapping("/dept")时直接省略前面的/dept
Result类可以以统一格式把数据上传到前端,并且是JSON格式(这是个工具类,直接导入pojo包下即可)
统一返回格式大致如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package com.test.springbootproject01.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;//响应码,1 代表成功; 0 代表失败
private String msg; //响应信息 描述字符串
private Object data; //返回的数据
//增删改 成功响应
public static Result success(){
return new Result(1,"success",null);
}
//查询 成功响应
public static Result success(Object data){
return new Result(1,"success",data);
}
//失败响应
public static Result error(String msg){
return new Result(0,msg,null);
}
}
|
Service层用来处理数据,需要用到注解@Service
1
2
3
4
5
6
7
8
9
|
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
public List<Dept> list(){
List<Dept> deptList = deptMapper.list();
return deptList;
}
}
|
最后到Mapper层用来和数据库对接,Mapper可以用XML来和数据库对接,也可以使用注解的方式,这里演示Mapper层配合xml格式调用数据
1
2
3
4
|
@Mapper
public interface DeptMapper {
public List<Dept> list();
}
|
xml文件如下
1
2
3
|
<select id="list" resultType="com.test.springbootproject01.pojo.Dept">
select * from springboottest.dept
</select>
|
这样Get请求从前端发送过来后,由后端Controller层接受请求,然后调用service层处理数据,然后service层再调用mapper层获取数据,最终处理完数据后返回给前端
安全性与认证
API接口使用JWT(JSON Web Token)进行认证,确保请求的安全性。如果登录成功就获得一个令牌,每次访问网站都会检查jwt令牌是否有效,同时可以给jwt令牌设置有效时限。
JWT认证示例(Spring Security集成JWT):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
public class JwtTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
// Validate and parse JWT token here
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
}
|
SpringBoot手搓jwt令牌认证
在使用前要在pom.xml中引入依赖
1
2
3
4
5
|
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
|
这是jwt令牌的工具类直接CV到pojo包下即可,key是秘钥,Time是令牌有效期,过期自动登出网站
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package com.test.springbootproject01.pojo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtHelper {
private String key = "Lucius";
private Integer Time=3600*1000;
public String getJwt(Claims claims){
String jwt= Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS256,key)
.setExpiration(new Date(System.currentTimeMillis()+Time))
.compact();
return jwt;
}
public Claims parseJwt(String jwt){
Claims claims=Jwts.parser()
//输入秘钥
.setSigningKey(key)
//给jwt令牌解码
.parseClaimsJws(jwt)
//获取claims对象
.getBody();
return claims;
}
}
|
为了让没有jwt令牌的用户无法访问网站,我们得使用拦截器,下面是springboot中的拦截器
我们要先配置这个拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
package com.test.springbootproject01.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.test.springbootproject01.pojo.JwtHelper;
import com.test.springbootproject01.pojo.Result;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override//目标方法运行前执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1获取请求路径
String url = request.getRequestURI();
log.info("拦截到请求:{}",url);
//如果是登录请求,放行
if(url.equals("/login")){
log.info("登录放行");
return true;
}
//2判断是否登录
String jwt=request.getHeader("token");
if(jwt==null){
log.info("未登录,拦截");
Result error=Result.error("NOT_LOGIN");
String notlogin= JSONObject.toJSONString(error);
response.getWriter().write(notlogin);
//返回false不放行
return false;
}
JwtHelper jwtHelper=new JwtHelper();
//3判断jwt是否合法
//解析jwt令牌时,如果解析失败,抛出异常,捕获异常,返回错误信息,如果解析成功,就可以放行
try {
jwtHelper.parseJwt(jwt);
} catch (Exception e) {
log.info("jwt无效");
Result error=Result.error("NOT_LOGIN");
String notlogin=JSONObject.toJSONString(error);
response.getWriter().write(notlogin);
return false;
}
log.info("jwt有效");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
|
正常写的话需要实现HandlerInterceptor接口中的preHandle方法,这个方法是在调用controller方法前执行的,在后端未向前端发送数据时拦截检查jwt令牌,jwt令牌的逻辑请看注释
写一个类名为WebConfig,然后配置拦截器的信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.test.springbootproject01.config;
import com.test.springbootproject01.interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration//@Configuration注解表示当前类是一个配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
//注入拦截器对象
private LoginCheckInterceptor loginCheckInterceptor;
@Override
//注册/添加拦截器
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor)
//添加拦截器拦截路径
.addPathPatterns("/**")
//除了/login以外的路径都要被拦截
.excludePathPatterns("/login");
}
}
|
然后回到登录的controller层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package com.test.springbootproject01.Controller;
import com.test.springbootproject01.Service.EmpService;
import com.test.springbootproject01.pojo.Emp;
import com.test.springbootproject01.pojo.JwtHelper;
import com.test.springbootproject01.pojo.Result;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.impl.DefaultClaims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/login")
public class LoginController {
@Autowired
private EmpService empService;
@PostMapping
public Result Login(@RequestBody Emp emp){
log.info("{}请求登录",emp);
Emp emp1=empService.login(emp);
//如果查有此人就开始准备制作令牌
if(emp1!=null){
JwtHelper jh=new JwtHelper();
Claims claims=new DefaultClaims();
claims.put("id",emp1.getId());
claims.put("username",emp1.getUsername());
claims.put("password",emp1.getPassword());
log.info("请求人用户名:{}",emp.getUsername());
log.info("请求人密码{}",emp.getPassword());
String jwt=jh.getJwt(claims);
return Result.success(jwt);
}
return Result.error("NOT_LOGIN");
}
}
|
java代码是这样的(