API接口文档使用教程

软件开发通识课()

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代码是这样的(

Licensed under CC BY-NC-SA 4.0