java脚手架系列3--统一返回格式、异常处理、swagger2

之所以想写这一系列,是因为之前工作过程中有几次项目是从零开始搭建的,而且项目涉及的内容还不少。在这过程中,遇到了很多棘手的非业务问题,在不断实践过程中慢慢积累出一些基本的实践经验,认为这些与业务无关的基本的实践经验其实可以复刻到其它项目上,在行业内可能称为脚手架,因此决定将此java基础脚手架的搭建总结下来,分享给大家使用。

注意由于框架不同版本改造会有些使用的不同,因此本次系列中主要使用基本框架是 spring-boo-2.3.12.RELEASE和spring-cloud.-Hoxton.SR12,所有代码都在commonFramework项目上:https://github.com/forever1986/commonFramework/tree/master

1 统一返回格式和异常处理

1.1 统一返回格式

由于现在项目越来越多都是前后端分离,因此后端对于前端来说就是一个提供数据查询的接口,那么这个时候,数据格式的统一就非常有必要,这样前端也能通过数据返回值统一处理。下面以springboot为框架,restful接口为规范的例子,实践中一般如何定义统一的数据返回值。

参考common-core子模块和manage-biz子模块

1)独立在common子模块下再建立common-core子模块。
2)定义一个泛型的Result,其中属性包括code(返回编码)、msg(返回消息)、data(数据)、total(数据总量)、page(当前页数)等等,下面以一个简单例子:

public class Result<T> implements Serializable {
    private String code;
    private T data;
    private String msg;
    private long total;
    //构造方法和方法省略,参考项目完整代码
}

3)定义ResultCode为常见的错误编码集合,方便使用

package com.demo.common.core.result;

import java.io.Serializable;

public enum ResultCode implements IResultCode, Serializable {
    SUCCESS("00000", "ok"),
    USER_ERROR("A0001", "用户端错误"),
    PARAM_IS_NULL("A0410", "请求必填参数为空"),
    SYSTEM_EXECUTION_ERROR("B0001", "系统执行出错");

    private String code;
    private String msg;

    public String getCode() {
        return this.code;
    }

    public String getMsg() {
        return this.msg;
    }

    public String toString() {
        return "{\"code\":\"" + this.code + '"' + ", \"msg\":\"" + this.msg + '"' + '}';
    }

    public static ResultCode getValue(String code) {
        ResultCode[] var1 = values();
        int var2 = var1.length;

        for(int var3 = 0; var3 < var2; ++var3) {
            ResultCode value = var1[var3];
            if (value.getCode().equals(code)) {
                return value;
            }
        }

        return SYSTEM_EXECUTION_ERROR;
    }

    private ResultCode(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    private ResultCode() {
    }
}

4)这样一个简单的统一返回值就出来,我们可以改造一下manage-biz子模块的controller方法,比如改造echo方法:

@SysLog(module= ModuleTypeEnum.MANAGE, description="测试echo")
@ApiOperation(value = "测试echo")
@GetMapping("/echo")
public Result<String> echo(@RequestParam String echo) {
    log.info("echo======================"+echo);
    log.info(nacosValueConstant.getValue());
    log.info(nacosValueConstant.getTest());
    return Result.success(demoService.echo(echo));
}

1.2 异常处理

异常的友好除了对于前端或者用户体验来说都是一种提升。以前都是通过拦截器方式做统一的异常处理,但处理起来还是有点麻烦,而基于springboot的restful风格的后端,springboot提供了@RestControllerAdvice(如果不是restful风格,则可以@ControllerAdvice)统一做全局异常处理,对于我们来说真的省了不少事,这里结合1.1统一返回格式,也将异常作为统一的返回格式。

参考common-exception子模块和manage-biz子模块

1)在common子模块下面新增common-exception子模块,以spring.factories方式发布
2)pom文件中引入以下依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.example</groupId>
    <artifactId>common-core</artifactId>
    <version>${project.version}</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

3)定义GlobalExceptionHandler,使用注解@RestControllerAdvice,然后定义各种异常处理

package com.demo.common.exception;

import com.demo.common.core.result.Result;
import com.demo.common.core.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {


	//兜底的异常处理
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({RuntimeException.class})
    public <T> Result<T> handleRuntimeException(RuntimeException e) {
        log.error("兜底异常,异常原因:{}", e.getMessage(), e);
        return Result.failed(ResultCode.SYSTEM_EXECUTION_ERROR);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({BizException.class})
    public <T> Result<T> handleBizException(BizException e) {
        log.error("业务异常,异常原因:{}", e.getMessage(), e);
        return e.getResultCode() != null ? Result.failed(e.getResultCode()) : Result.failed(e.getMessage());
    }
}

4)可以定义各类自定义异常,比如我们定义一个BizException

package com.demo.common.exception;

import com.demo.common.core.result.IResultCode;

public class BizException extends RuntimeException{

    public IResultCode resultCode;

    public BizException(IResultCode errorCode) {
        super(errorCode.getMsg());
        this.resultCode = errorCode;
    }

    public BizException(String message) {
        super(message);
    }

    public BizException(String message, Throwable cause) {
        super(message, cause);
    }

    public BizException(Throwable cause) {
        super(cause);
    }

    public IResultCode getResultCode() {
        return this.resultCode;
    }
}

5)在spring.factories中注入GlobalExceptionHandler
6)在manage-biz中引入common-exception
7)在manage-biz中的DemoController写一个测试接口:

@SysLog(module= ModuleTypeEnum.MANAGE, description="测试全局异常处理")
@ApiOperation(value = "测试全局异常处理")
@GetMapping("/testexception")
public Result<String> testException(Boolean isNotNull) {
    log.info("call testException");
    if(isNotNull==null || !isNotNull){
        throw new BizException(ResultCode.PARAM_IS_NULL);
    }
    return Result.success("ok");
}

2 swagger2

现在项目基本上都是前后端分离的,特别是后端提供Rest API接口,因为为了适用于不同端(PC、IOS、Android等),因此后端都是统一一个(可能会采用不同api分流或者区分)。所以后端写的api需要调试和文档就非常重要,调试可以使用postman,但是每个接口都要自己建立一个request,文档可以使用javadoc,但是更新也是一个问题。这个时候swagger2顺势而生,它同时解决了调试和文档的问题。
Swagger是一套围绕Open API 规范构建的开源工具,可以帮助设计,构建,记录和使用REST API。简单理解就是后端项目通过配置Swagger,则通过在发布的API中加入注解,自动就会生成一个可视化的调试和文档界面。

2.1 代码实践

Swagger是一个辅助功能,因此可以提炼为一个common子模块

请参考common-swagger子模块和manage-biz子模块

1)在common子模块下新建common-swagger子模块(以spring.factories方式发布)
2)在pom配置如下依赖:

<dependency>
     <groupId>io.springfox</groupId>
     <artifactId>springfox-swagger2</artifactId>
 </dependency>
 <dependency>
     <groupId>io.springfox</groupId>
     <artifactId>springfox-swagger-ui</artifactId>
 </dependency>
 <!-- 只是为了引入Slf4j -->
 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <optional>true</optional>
 </dependency>

3)设置SwaggerConfiguration

package com.demo.common.swagger;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@Slf4j
@Profile({"dev","test"})
@EnableSwagger2
public class SwaggerConfiguration {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.demo.manage.biz.controller"))//这里填写项目package
                .paths(PathSelectors.any())
                .build();
    }//springfox为我们提供了一个Docket(摘要的意思)类,我们需要把它做成一个Bean注入到spring中, 显然,我们需要一个配置文件,并通过一种方式(显然它会是一个注解)告诉程序,这是一个Swagger配置文件。

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("demo API")
                .description("rest api 文档构建利器")
                .version("1.0")
                .build();
    }
}

这里要说明一下,其中@Profile({“dev”,“test”})配置了dev、test环境才开启(生产环境不开启swagger);
4)在业务子模块manage-biz的pom中引入common-swagger子模块

<dependency>
    <groupId>org.example</groupId>
    <artifactId>common-swagger</artifactId>
    <version>${project.version}</version>
</dependency>

5)在需要发布到swagger中的API接口(controller层),增加注解:@ApiOperation(value = “测试echo”)即可

2.2 swagger常见注解

1)@Api:可以放在类上面,对类的基本描述(可以作为分组使用)
2)@ApiOperation:可以放在某个方法上面,对方法的基本描述
3)@ApiImplicitParam:注解用在方法上,用于描述方法需要的参数
4)@ApiResponses:对返回值进行说明
5)@ApiModel和@ApiModelProperty:配置对象的描述信息
6)swagger还可以配置访问权限。但是总体来说,生产环境最好不要也没必要配置swagger。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值