3 Star 6 Fork 3

shawn / graceful-response

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

1. 简介

Graceful Response基于graceful-response v3.0 定制调整,提供一个Spring Boot体系下的优雅响应处理器,提供一站式统一返回值封装、全局异常处理、自定义异常错误码等功能,使用Graceful Response进行web接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。

定制调整内容主要包括:

  • 异常体系,添加了Http状态码处理,自定义异常中,须添加HttpStatus属性,接口发生异常时,响应头自动设置异常中定义的状态码(原graceful-response中未对响应状态码进行处理)
  • 默认取消了对响应体JSON的重新包装,API 接口中定义什么样的实体类,就返回对应的JSON报文
  • 将响应体字段的命名方式,java类中驼峰命名的属性,默认统一处理成"_"连接方式【TODO】 本定制调整后,接口响应中状态码、响应报文,更符合Restful API规范

本项目使用案例见:https://gitee.com/shawn_lxc/graceful-response-example

2. Java Web API接口数据返回的现状及解决方案

通常我们进行Java Web API接口时,大部分的Controller代码是这样的:

public class Controller {
    @GetMapping("/query")
    @ResponseBody
    public Response query(Parameter params) {

        Response res = new Response();
        try {
            //1.校验params参数,非空校验、长度校验
            if (illegal(params)) {
                res.setCode(1);
                res.setMsg("error");
                return res;
            }
            //2.调用Service的一系列操作
            Data data = service.query(params);
            //3.将操作结果设置到res对象中
            res.setData(data);
            res.setCode(0);
            res.setMsg("ok");
            return res;
        } catch (BizException1 e) {
            //4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
            res.setCode(1024);
            res.setMsg("error");
            return res;
        } catch (BizException2 e) {
            //4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
            res.setCode(2048);
            res.setMsg("error");
            return res;
        } catch (Exception e) {
            //4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
            res.setCode(1);
            res.setMsg("error");
            return res;
        }
    }
}

这段代码存在什么问题呢?

  • 真正的业务逻辑被冗余代码淹没,真正执行业务的代码只有
Data data=service.query(params);

其他代码不管是正常执行还是异常处理,都是为了异常封装、把结果封装为特定的格式,例如以下格式:

{
  "code": 0,
  "msg": "ok",
  "data": {
    "id": 1,
    "name": "username"
  }
}

这样的逻辑每个接口都需要处理一遍,都是繁琐的重复劳动。

  • 在专门提供开放API的场景,响应结果封装,并没有标准规范,而主流的开放API提供商,响应结果都是不予封装的
  • 没有对响应状态码进行细化处理,当发生业务异常时,响应状态码统一设置为500,甚至有些项目对异常捕获后,响应码仍为200

现在,在引入Graceful Response组件后,我们只要直接返回业务结果,Graceful Response即可自动完成response的响应状态码处理以及响应报文的规范化处理。

3. 快速入门

3.1 引入maven依赖

graceful-response已发布至maven中央仓库,可以直接引入到项目中,maven依赖如下:


<dependency>
  <groupId>com.gitee.shawn_lxc</groupId>
  <artifactId>graceful-response</artifactId>
  <version>1.0.0</version>
</dependency>

目前最新版本为1.0.0

3.2 在启动类中引入@EnableGracefulResponse注解


@EnableGracefulResponse
@SpringBootApplication
public class ExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }
}

3.3 Controller方法直接返回结果

  • 普通的查询

import org.springframework.web.bind.annotation.ResponseStatus;

@Controller
public class Controller {
  @RequestMapping("/get")
  @ResponseStatus(HttpStatus.OK) 
  @ResponseBody
  public UserInfoView get(Long id) {
    log.info("id={}", id);
    return UserInfoView.builder().id(id).name("name" + id).build();
  }
}

这个接口直接返回了 UserInfoView的实例对象,调用接口时,Graceful Response将自动返回以下格式报文:

{
  "id": 1,
  "name": "name1"
}

每个请求API添加正常响应状态码,如上接口正常返回时,响应码为200。

接口正常状态时,响应码定义,建议采用如下规范:

200 OK [GET]: 服务器端成功返回用户请求的数据;批量重启、批量删除操作成功
201 CREATED [POST/PUT/PATCH]: 用户新建或修改数据成功。
202 Accepted 表示一个请求已经进入后台排队(一般是异步任务)。
204 NO CONTENT -[DELETE]: 用户删除数据成功、修改数据成功。
  • 返回值为空的场景
public class Controller {
    @RequestMapping("/void")
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public void testVoidResponse() {
        //业务操作
    }
}

testVoidResponse方法的返回时void,调用这个接口时,将返回:

{
  "status": {
    "code": "200",
    "msg": "success"
  },
  "payload": {}
}

3.4 Service方法业务处理

在引入Graceful Response后,Service将:

  • 接口直接返回业务数据类型,而不是Response
public interface ExampleService {
    UserInfoView query1(Query query);
}
  • Service接口实现类中,直接抛业务异常,接口调用异常时将直接返回错误码和错误提示
public class ExampleServiceImpl implements ExampleService {
    @Resource
    private UserInfoMapper mapper;

    UserInfoView query1(Query query) {
        UserInfo userInfo = mapper.findOne(query.getId());
        if (Objects.isNull(userInfo)) {
            //这里直接抛自定义异常
            throw new NotFoundException();
        }
        //……后续业务操作
    }
}
/**
 * NotFoundException的定义,使用@ExceptionMapper注解修饰
 * code:代表接口的异常码
 * msg:代表接口的异常提示
 */
@ExceptionMapper(httpStatus = HttpStatus.NOT_FOUND, code = "1404", msg = "找不到对象")
public class NotFoundException extends RuntimeException {

}

当Service方法抛出NotFoundException异常时,接口将响应状态码设置404(HttpStatus.NOT_FOUND),并将错误码和错误信息作为响应体返回,不需要手工set。

{
  "error_code": "1404",
  "error_msg": "找不到对象"
}

验证:启动example工程后,请求http://localhost:8811/example/notfound

3.5 通用异常类和通用工具类

@ExceptionMapper设计的初衷,是将状态码、异常与错误码关联起来,用户只需要抛异常,不需要再关注异常与状态码、错误码的对应关系。

在不自定义新异常类的情况下,也能可以按照预期返回状态码、错误码和异常信息,利用GracefulResponseException异常类,用户只需要抛出该异常即可。

public class Service {
  
  public void method() {
    throw new GracefulResponseException(HttpStatus.XXX, "自定义的错误码","自定义的错误信息");
  }
}

为简化使用,提供了GracefulResponse通用工具类,在需要抛出GracefulResponseException时,只需要调用raiseException方法即可。 这样做的目的是将用户的关注点从异常转移到错误码。

示例如下:

public class Service {

    public void method() {
        
        //当condition==true时,抛出GracefulResponseException异常,返回自定义的错误码和错误信息
        if (condition) {
            GracefulResponse.raiseException(HttpStatus.NOT_FOUND, "自定义的错误码", "自定义的错误信息");
        }
    }
}

3.6 参数校验异常以及错误码

引入Graceful Response后,使用@ValidationStatusCode注解,可以非常方便地支持validation校验异常。

@ValidationStatusCode注解目前只有一个code属性,用于指定参数校验异常时的错误码,错误提示则取自validation校验框架。参数校验异常时,状态码均为默认值是400(HttpStatus.BAD_REQUEST)。

  • 对入参类进行参数校验
@Data
public class UserInfoQuery {

    @NotNull(message = "userName is null !")
    @Length(min = 6, max = 12)
    @ValidationStatusCode(code = "520")
    private String userName;

}

userName字段任意一项校验不通过时,接口将会返回异常码520和校验注解中的message,并将状态码设置为400:

{
  "status": {
    "code": "520",
    "msg": "userName is null !"
  },
  "payload": {}
}

详细见example工程ExampleController的validateDto方法 http://localhost:8811/example/validateDto

注意:@ValidationStatusCode校验参数对象字段的情况,code取值顺序为:会先取字段上的注解,再去该属性所在对象的类(即UserInfoQuery类)上的注解,再取全局配置的参数异常码gr.defaultValidateErrorCode,最后取默认的全局默认的错误码(默认code=1)

  • 直接在Controller中校验方法入参

直接在Controller方法中进行参数校验:

public class ExampleController {

  @RequestMapping("/validateMethodParam")
  @ResponseBody
  @ValidationStatusCode(code = "1314")
  public void validateMethodParam(@NotNull(message = "userId不能为空") Long userId,
                                  @NotNull(message = "userName不能为空") Long userName) {
      //省略业务逻辑
  }
}

当userId、或者userName校验不通过时,将会返回code=1314,msg为对应的校验信息。

{
  "error_code": "1314",
  "error_msg": "userId不能为空"
}

详细见example工程ExampleController的validateMethodParam方法 http://localhost:8811/example/validateMethodParam

注意:@ValidationStatusCode校验Controller方法参数字段的情况,code取值顺序为:会先取当前方法上的注解,再去该方法所在类(即ExampleController类)上的注解,再取全局配置的参数异常码gr.defaultValidateErrorCode,最后取默认的全局默认的错误码(默认code=1)

4. 进阶用法

4.1 Graceful Response异常错误码处理

以下是使用Graceful Response进行异常、错误码处理的开发步骤。

创建自定义异常,采用 @ExceptionMapper注解修饰,注解的 code属性为返回码,msg属性为错误提示信息


@ExceptionMapper(httStatus = HttpStatus.INTERNAL_SERVER_ERROR, code = "1007", msg = "有内鬼,终止交易")
public static final class RatException extends RuntimeException {

}

Service执行具体逻辑,需要抛异常的时候直接抛出去即可,不需要在关心异常与错误码关联的问题

public class Service {
    public void illegalTransaction() {
        //需要抛异常的时候直接抛
        if (hasRat()) {
            logger.error("有内鬼终止交易");
            throw new RatException();
        }
        doIllegalTransaction();
    }
}

Controller调用Service

public class Controller {
    @RequestMapping("/test3")
    public void test3() {
        logger.info("test3: RuntimeException");
        //Controller中不会进行异常处理,也不会手工set错误码,只关心核心操作,其他的统统交给Graceful Response
        exampleService.illegalTransaction();
    }
}

在浏览器中请求controller的/test3方法,有异常时将会返回:

{
  "error_code": "1007",
  "error_msg": "有内鬼,终止交易"
}

4.2 外部异常别名

案例工程( https://gitee.com/shawn_lxc/graceful-response-example.git )启动后, 通过浏览器访问一个不存在的接口,例如 http://localhost:8811/example/get2?id=1

如果没开启Graceful Response,将会跳转到404页面页面,主要原因是应用内部产生了 NoHandlerFoundException异常。如果开启了Graceful Response,默认会返回code=1的错误码,并且状态码为404。

这类非自定义的异常,如果需要自定义一个错误码返回,将不得不对每个异常编写Advice逻辑,在Advice中设置状态码、错误码和提示信息,这样做非常繁琐。

Graceful Response可以非常轻松地解决给这类外部异常定义错误码和提示信息的问题。

以下为操作步骤:

  • 创建异常别名,并用 @ExceptionAliasFor注解修饰

import org.springframework.http.HttpStatus;

@ExceptionAliasFor(httpStatus = HttpStatus.NOT_FOUND, code= "1404", msg = "not found", aliasFor = NoHandlerFoundException.class)
public class NotFoundException extends RuntimeException {
}

code:捕获异常时返回的错误码

msg:为提示信息

aliasFor:表示将成为哪个异常的别名,通过这个属性关联到对应异常。

  • 注册异常别名

创建一个继承了AbstractExceptionAliasRegisterConfig的配置类,在实现的registerAlias方法中进行注册。


@Configuration
public class GracefulResponseConfig extends AbstractExceptionAliasRegisterConfig {

    @Override
    protected void registerAlias(ExceptionAliasRegister aliasRegister) {
        aliasRegister.doRegisterExceptionAlias(NotFoundException.class);
    }
}
  • 浏览器访问不存在的URL

再次访问 http://localhost:8811/example/get2?id=1 ,服务端将返回以下json,正是在ExceptionAliasFor中定义的内容

{
  "error_code": "1404",
  "error_msg": "not found"
}

4. 常用配置

  • gr.printExceptionInGlobalAdvice

    是否打印异常日志,默认为true

  • gr.defaultErrorCode

    自定义的失败错误码,默认为1

  • gr.defaultErrorMsg

    自定义的失败提示,默认为error

  • gr.defaultValidateErrorCode

    全局的参数校验错误码,默认等于gr.defaultErrorCode

MIT License Copyright (c) 2021 feiniaojin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

fork from https://github.com/feiniaojin/graceful-response 展开 收起
Java
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/shawn_lxc/graceful-response.git
git@gitee.com:shawn_lxc/graceful-response.git
shawn_lxc
graceful-response
graceful-response
main

搜索帮助

53164aa7 5694891 3bd8fe86 5694891