# RestResponse REST响应服务

MornBoot提供注解式接口响应控制,用于简化甚至免除响应消息的构建过程。响应消息默认支持国际化、异常解释。

Since:v1.2.2

# 必要配置

pom.xml


<dependencies>
  <!--Web模块-->
  <dependency>
    <groupId>site.morn.boot</groupId>
    <artifactId>morn-boot-web</artifactId>
    <version>${morn.version}</version>
  </dependency>
</dependencies>

# 推荐配置

pom.xml


<dependencies>
  <!--异常解释[可选]-->
  <dependency>
    <groupId>site.morn.boot</groupId>
    <artifactId>morn-boot-interpreter</artifactId>
    <version>${morn.version}</version>
  </dependency>
</dependencies>

# 构建响应消息

# 构建正常响应

使用@RestResponse注解标注@RestController的类或方法,接口映射方法将会返回标准的RestMessage格式消息。


@RestResponse
@RestController
@RequestMapping("test/rest")
public class TestRestController {

  /**
   * 返回用户数据
   */
  @GetMapping("data")
  public TestUser returnData() {
    return new TestUser(1L, "Timely");
  }
}

响应结果

{
  "status": 200,
  "code": "success",
  "level": "info",
  "message": "操作成功",
  "data": {
    "id": 1,
    "departmentId": null,
    "username": "Timely",
    "password": null
  }
}

当引入morn-boot-web模块后,MornBoot内置了RestControllerAdviceResponseBodyAdvice用于处理接口映射的响应体。因此建议在项目中移除对响应体的全局处理,在后续文档中,将会介绍如何构建自定义响应消息。

# 构建异常响应

直接抛出异常即可,同时也支持使用ApplicationMessage构建异常。以RuntimeException为例:


@RestResponse
@RestController
@RequestMapping("test/rest")
public class TestRestController {

  /**
   * 抛异常
   */
  @GetMapping("ex")
  public SerialMessage returnException() {
    throw new RuntimeException("This is exception.");
  }
}

Response Body

{
  "status": 500,
  "code": "failure",
  "level": "error",
  "message": "This is exception.",
  "data": null
}

当Rest方法返回值的声明类型是void|String,且实际返回类型是void|String|null|throw,可能导致响应text/plain消息,而不是application/json,比较好的做法是将返回值声明为SerialMessage。响应消息默认支持国际化,参考:RestMessage

# 解析异常消息

当引入morn-boot-interpreter模块后,系统会自动解析已收录的第三方异常,并暴露给开发人员。以javax.validation 为例:


@RestResponse
@RestController
@RequestMapping("test/rest")
public class TestRestController {

  /**
   * 抛异常 - 可解释异常
   */
  @GetMapping("resolvable/ex")
  @RestResponse
  public void returnResolvableException(@Valid TestUser user) {
  }
}

使用无效参数访问test/rest/resolvable/ex接口,会得到易于识别的提示信息。

{
  "status": 500,
  "code": "validate",
  "level": "error",
  "message": "username must not be null",
  "data": null
}

# 构建自定义响应

业务框架中通常定义了自己的响应消息格式,MornBoot也支持将RestMessage转换为业务消息格式。 以百度的REST消息为例,在@RestResponse 注解中指定转换类型,即可返回指定格式的消息。


@RestResponse
@RestController
@RequestMapping("test/rest")
public class TestRestController {

  /**
   * 返回用户数据 - 百度格式
   */
  @GetMapping("baidu/data")
  @RestResponse(BaiduMessage.class)
  public TestUser returnBaiduData() {
    return new TestUser(2L, "Baidu");
  }
}

Response Body

百度error为0表示访问成功,其它字段也与RestMessage略有差别。

{
  "error": "0",
  "msg": "操作成功",
  "data": {
    "creator": null,
    "createTime": null,
    "modifier": null,
    "modifyTime": null,
    "departmentId": null,
    "id": 2,
    "username": "Baidu",
    "password": null,
    "display": null
  }
}

为达到转换的效果,还需要编写消息类和转换类。

点击查看代码
/**
 * 百度REST消息
 */
@Getter
@Setter
@ToString
public class BaiduMessage {

  /**
   * 状态码
   */
  private String error;

  /**
   * 消息内容
   */
  private String msg;

  /**
   * 消息数据
   */
  private Object data;
}

/**
 * 百度消息转换器
 */
@Component
@Source(RestMessage.class)
@Target(BaiduMessage.class)
public class BaiduMessageConverter implements RestMessageConverter<BaiduMessage> {

  @Override
  public BaiduMessage convert(RestMessage restMessage) {
    BaiduMessage baiduMessage = new BaiduMessage();
    baiduMessage.setError(RestMessageConstants.isSuccess(restMessage.getStatus()) ? "0" : "-1");
    baiduMessage.setMsg(restMessage.getMessage());
    baiduMessage.setData(restMessage.getData());
    return baiduMessage;
  }

  @Override
  public RestMessage revert(BaiduMessage baiduMessage) {
    RestMessage restMessage = new SimpleRestMessage();
    boolean success = isSuccess(baiduMessage);
    restMessage.setStatus(success ? RestMessageConstants.SUCCESS : RestMessageConstants.FAILURE);
    restMessage.setLevel(success ? RestMessageLevel.INFO : RestMessageLevel.ERROR);
    restMessage.setCode(baiduMessage.getError());
    restMessage.setMessage(baiduMessage.getMsg());
    restMessage.setData(baiduMessage.getData());
    return restMessage;
  }

  private boolean isSuccess(BaiduMessage baiduMessage) {
    return Objects.equals(baiduMessage.getError(), "0");
  }
}

# 响应消息标准化

除了将RestMessage转换为业务消息格式外,MornBoot也支持反向转换,将业务消息转换为RestMessage对象。


@RestResponse
@RestController
@RequestMapping("test/rest")
public class TestRestController {

  /**
   * 返回用户数据 - Morn格式
   */
  @GetMapping("morn/data")
  @RestResponse(RestMessage.class)
  public BaiduMessage returnMornData() {
    TestUser user = new TestUser(3L, "Morn");
    BaiduMessage baiduMessage = new BaiduMessage();
    baiduMessage.setError("0");
    baiduMessage.setData(user);
    return baiduMessage;
  }
}

# Rest全局配置

# 配置说明

# 若force-serial为true,无论是否标注@RestResponse,都会响应为SerialMessage
morn.rest.force-serial=false
# 全局响应类型(默认),优先级:方法注解 > 类注解 > 全局配置
morn.rest.response-class=site.morn.rest.RestMessage