REST风格的后端接口规范

5,749 阅读5分钟

写在前面

众所周知规范的HTTP接口由请求方式、请求参数、请求地址、响应数据、统一异常拦截构成。所以本文从这五个方面入手,规范接口,减少前后端联调时间!本文基于REST的风格规范后端接口,对REST的说明可以参考这篇文章:www.ruanyifeng.com/blog/2011/0…

请求方式

使用HTTP的请求方式代表操作资源的动作,定义接口时要根据业务选择适合的请求方式,常见的HTTP请求有5种:

1. GET: 从服务器查询出一个或多个数据
2. PUT: 更新服务器完整资源
3. PATCH: 更新服务器部分资源
4. POST: 新增资源
5. DELETE: 删除资源

请求地址

请求地址就是URL,我们已经用HTTP请求代表操作资源的动作了,所以我们在URL中就不应该出现动作。

假设场景设计一个接口操作公司的部门和员工,下面给出规范的URL的例子:

查询示例

1. 查询所有部门
GET  /department
2. 根据部门编号查询部门信息
GET  /department/{id}
3. 根据部门编号,员工编号查询员工
GET /account/{id}/department/{id}
4. 分页查询部门
GET  /department?currentPage=1&pageSize=10

新增示例

1. 新增部门
POST /department
2. 新增员工
POST /account

更新示例

1. 更新部门除主键的所有信息
PUT  /department
2. 更新部门的名称
PUT /department

删除示例

1. 删除部门
DELETE /department/{id}

1. 删除某部门中名叫狗剩的员工
DELETE /account/{name}/department/{id}

请求参数

新增和更新

前端在调用新增和更新接口时传递的是一个对象,所以当请求参数个数等于1时,可以直接接收此参数,当参数大于1时需要新建一个DTO对象来接收
方便接口调用方调用,我们要把对象和属性写上详细的swagger注释( 该类的作用、该值的作用、格式规则、示例)。

具体示例参考下面代码,节约篇幅省去了getter和setter:

@ApiModel("部门对象")
public class DepartmentDto {
    @ApiModelProperty(value ="部门id、不得超过6位",example = "1")
    private Integer id;
    @ApiModelProperty(value ="部门名称、不得超过10位",example = "市场部")
    private String name;


}

查询和删除

查询和删除不能新建一个对象来接受,所以只能在接口处定义swagger注释(该值的作用、格式规则、示例),并且描述该接口的作用、错误码的返回、成功的返回;

 @ApiOperation(value = "根据部门id查询部门信息", notes = "根据部门id查询部门信息")
    @GetMapping("/{id}")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "查询成功"),
            @ApiResponse(code = 500, message = "systemError<br>"
                    + "lengthError<br>")})
    @ApiImplicitParam(name = "id",value = "部门id、不得超过6位",example = "1")
    public Result<DepartmentDto> getList(@PathVariable("id") String id){
        //假装查询哈
        return Result.success("查询成功",new DepartmentDto());
    }

响应数据

前后端分离时,我们需要定义统一的返回数据格式,并且对异常也要全局拦截。

统一返回数据对象

统一返回对象应包括以下字段,code:Http状态码message:错误码data:返回数据

  • 这三个字段应加上swagger注释;
  • 构造方法私有化、新建静态的构建方法以便快速得到;
  • 为打印日志方便可以重写toString方法;
@ApiModel("统一返回对象")
public class Result<T> {

    @ApiModelProperty("HTTP 请求状态码")
    private String code;
    @ApiModelProperty("返回的错误码")
    private String message;
    @ApiModelProperty("返回数据")
    private T data;
    private static final String success = HttpStatus.OK.value()+"";
    private static final String fail = HttpStatus.INTERNAL_SERVER_ERROR.value()+"";

    private Result(String code, String message, T t) {
        this.code = code;
        this.message = message;
        this.data = t;
    }

    public static Result success(String message,Object data){
        return new Result(success,message,data);
    }
}

全局异常拦截

异常码

新建一个异常码的枚举,方便管理存储和返回异常;代码中含有no和code的字段,code用于返回前端,no的可以由自己的业务规则生成,将这个no打印在日志中,方便日后排查问题;

public enum ExceptionEnum {

	SYSTEM_ERROR("010-1000000","systemError");


	private final String no;
	private final String code;



	ExceptionEnum(String no, String code) {
		this.no = no;
		this.code = code;
	}

	public String getNo() {
		return no;
	}


	public String getCode() {
		return code;
	}

	@Override
	public String toString() {
		return this.code;
	}

}

全局异常类

新建全局异常码枚举,使用异常码管理异常;

public class BusinessException extends RuntimeException {
	
	private static final long serialVersionUID = 1L;
	private ExceptionEnum exceptionEnum;
	private Object data ;

    /**
     * Instantiates a new Iam base exception.
     */
    public BusinessException() {
    	
    }


  
    public BusinessException(ExceptionEnum exceptionEnum) {
        super(exceptionEnum.getCode());
        this.exceptionEnum = exceptionEnum;
    }
}

异常拦截

全局异常拦截,如果是业务异常则返回业务返回的错误码;如果是系统内部的错误比如空指针异常等等则抛出系统异常(这样处理是为了统一返回,方便前端处理,空指针异常只是示例,如果真的抛出空指针异常就要好好思考下为什么没判空!!!)

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public Result exception(Exception e) {
        return Result.fail(ExceptionEnum.SYSTEM_ERROR.getCode());
    }

    @ExceptionHandler(BusinessException.class)
    public Result exception(BusinessException e) {
        return Result.fail(e.getCode());
    }

}

接口规范示例

GET 和DELETE 接口示例

添加@ApiOperation 描述该方法
添加@ApiResponses 描述成功和失败的message 、Http请求的状态码
添加@ApiImplicitParam 描述传入参数、格式限制、默认值

 @ApiOperation(value = "根据部门id查询部门信息", notes = "根据部门id查询部门信息")
    @GetMapping("/{id}")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "查询成功"),
            @ApiResponse(code = 500, message = "systemError<br>"
                    + "lengthError<br>")})
    @ApiImplicitParam(name = "id",value = "部门id、不得超过6位",example = "1")
    public Result<DepartmentDto> getList(@PathVariable("id") String id){
        //假装查询哈
        return Result.success("查询成功",new DepartmentDto());
    }

POST、PUT、PATCH接口示例

@ApiOperation(value = "新增部门信息", notes = "新增部门信息")
    @PostMapping("/")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "新增成功"),
            @ApiResponse(code = 500, message = "systemError<br>"
                    + "lengthError<br>")})
    public Result add(@RequestBody DepartmentDto dto){
        //假装新增哈
        return Result.success("新增成功",null);
    }

API文档

按照上述的代码所写,得到的swagger文档如图:

代码

示例代码已经上传至www.fizzed.top/archives/ji…

写在最后

欢迎大家关注我的公众号【有一只基的程序猿】,一起交流Java、大数据,文章对应的脑图也会放在公众号里。 点关注,不迷路!!!

博客地址: www.fizzed.top