ContiNew

Web 开发

Web 开发基础模块:跨域(CORS)、全局异常与统一响应封装、Query 参数转换器与 Tomcat 服务器增强的自动配置。

continew-starter-web 是 ContiNew Starter 提供的 Web 开发基础模块,为 Spring Boot Web 应用提供常用能力的自动配置与最佳实践,主要包含以下内容。

  • 跨域支持:通过配置快速启用和定制跨域过滤器 CorsFilter;
  • 统一响应处理:内置响应数据封装、全局异常处理、异常别名与国际化,无第三方响应组件依赖;
  • Query 参数转换器:Date、LocalDate、LocalTime、LocalDateTime、BaseEnum 类型参数自动转换;
  • 服务器增强:基于 Tomcat 服务器,支持全局禁用不安全的 HTTP 请求方法。

链路追踪(Trace)是独立模块 continew-starter-trace,不在本模块中,详见 Trace 链路追踪。

引入依赖

<dependency>
    <groupId>top.continew.starter</groupId>
    <artifactId>continew-starter-web</artifactId>
</dependency>

跨域配置

前后端分离开发时,跨域是避不开的配置。模块提供了跨域过滤器自动配置(CorsAutoConfiguration),配置项对应类为 top.continew.starter.web.autoconfigure.cors.CorsProperties,默认不启用。

continew-starter:
  web:
    cors:
      # 是否启用跨域配置(默认:false)
      enabled: true
      # 允许跨域的域名(Ant 风格通配符模式,与 allowed-origins 互斥,配置后仅本项生效)
      allowed-origin-patterns:
        - https://*.example.com
      # 允许跨域的域名(默认:*)
      allowed-origins: '*'
      # 允许跨域的请求方式(默认:*)
      allowed-methods: '*'
      # 允许跨域的请求头(默认:*)
      allowed-headers: '*'
      # 允许跨域的响应头
      exposed-headers: '*'

当需要携带 Cookie 等凭据信息时,CORS 规范不允许 allowed-origins 与 allow-credentials 同时为 *,此时推荐使用 allowed-origin-patterns(如 https://*.example.com)进行精确匹配。自动配置会在 allowed-origin-patterns 不含 *、或 allowed-origins 为具体域名时自动开启 allowCredentials;预检请求缓存时间(maxAge)固定为 1800 秒。

统一响应处理

统一响应处理由本模块内置实现,包含响应数据封装、全局异常处理、异常别名与国际化。3.0 起不再依赖 Graceful Response,默认响应类为 top.continew.starter.web.model.R,可通过 GlobalResponseFactory 替换。

启用统一响应处理

统一响应处理不参与自动配置扫描,必须通过 @EnableGlobalResponse 显式启用。

@EnableGlobalResponse
@SpringBootApplication
public class ExampleApplication {

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

全局配置

配置项对应类为 top.continew.starter.web.autoconfigure.response.GlobalResponseProperties。模块已内置一套默认值(见 continew-starter-web/src/main/resources/default-response.yml),常用配置如下。

continew-starter:
  web:
    response:
      # 是否开启国际化(默认:false)
      i18n: false
      # 失败时的 HTTP 状态码(默认:200,建议业务状态码与通信状态码区分)
      default-http-status-code-on-error: 200
      # 成功响应码(默认:0)
      default-success-code: 0
      # 成功提示(默认:ok)
      default-success-msg: ok
      # 失败响应码(默认:1)
      default-error-code: 1
      # 失败提示(默认:error)
      default-error-msg: error
      # 参数校验失败响应码(默认取 default-error-code)
      default-validate-error-code:
      # 是否打印异常日志(默认:false)
      print-exception-in-global-advice: false
      # 是否将原生异常错误信息填充到响应信息中(默认:false)
      origin-exception-using-detail-message: false
      # 例外包路径(支持数字、* 和 ** 通配符匹配),该包路径下的 Controller 将被忽略处理
      exclude-packages:
        - io.swagger.**
        - org.springdoc.**
        - org.springframework.boot.actuate.*

响应结构

响应类 top.continew.starter.web.model.R 的结构如下。

{
  "code": "0",
  "msg": "ok",
  "success": true,
  "timestamp": 1691453288000,
  "data": {}
}

success 由响应码与 default-success-code 比较得出,timestamp 取序列化时刻。控制器直接返回 R 时不会被二次包装。

自定义响应结构

响应对象的创建、识别与信息改写都通过 GlobalResponseFactory 完成。需要不同字段结构时,声明一个该接口的 Bean 即可,无需修改 Starter 代码。

@Bean
public GlobalResponseFactory globalResponseFactory() {
    return new GlobalResponseFactory() {

        @Override
        public Object newSuccess(Object data) {
            return new CustomResponse(200, data, null);
        }

        @Override
        public Object newFailure(String code, String msg) {
            return new CustomResponse(code, null, msg);
        }

        @Override
        public boolean isResponse(Object body) {
            return body instanceof CustomResponse;
        }

        @Override
        public String getCode(Object body) {
            return String.valueOf(((CustomResponse) body).getStatus());
        }

        @Override
        public void setMessage(Object body, String message) {
            ((CustomResponse) body).setMessage(message);
        }
    };
}

自定义响应用于包装控制器返回值时,应与控制器声明的返回类型是不同的类(例如自定义 CustomResponse),避免 JSON 序列化引擎按控制器的泛型声明做强制类型转换。

排除统一封装

文件下载等场景需要直接返回原始数据时,在接口方法、控制器类或返回类型上添加 @ExcludeFromGlobalResponse 注解即可排除统一封装。

@ExcludeFromGlobalResponse
@GetMapping("/download")
public void download(HttpServletResponse response) {
    // 直接输出文件流
}

也可以通过 exclude-packages、exclude-urls、exclude-return-types 按包路径、请求 URL 或返回类型批量排除。返回 ResponseEntity、文件流等非 JSON 响应同样不会被包装。

异常别名

需要把第三方异常映射为固定响应码时,可使用配置方式:

continew-starter:
  web:
    response:
      exception-alias-config-map:
        "[java.lang.IllegalStateException]":
          code: 4001
          msg: 状态异常
          http-status-code: 418

也可以在异常类上标注 @ExceptionAliasFor,并通过 ExceptionAliasRegister 注册:

@ExceptionAliasFor(aliasFor = IllegalStateAliasException.class, code = "4001", msg = "状态异常")
public class IllegalStateAliasException extends RuntimeException {
}
@Bean
public ExceptionAliasRegister exceptionAliasRegister() {
    return new ExceptionAliasRegister().doRegisterExceptionAlias(IllegalStateAliasException.class);
}

aliasFor 指定该别名配置生效的异常类型(可列多个,注册后这些类型及其子类生效),需要把自定义异常自身也列进去。

业务方自定义的 @RestControllerAdvice 处理器优先于全局异常处理器,二者不会产生重复包装。注意 Starter 的全局异常处理器为 @Order(200)(约束校验处理器为 @Order(190)),业务方 advice 需声明小于对应值的 @Order 才会优先;未声明 @Order 时按 LOWEST_PRECEDENCE 处理,会被 Starter 的处理器抢先。

国际化

国际化沿用 Spring Boot 的标准方案,Starter 不自建 MessageSource,只读取应用已有的 MessageSource Bean。开启 i18n 后,仅当响应信息为空或等于默认成功/失败提示(如 ok、error)时,才按 code 查找并替换;参数校验的字段级提示与异常明细信息不会被覆盖,未匹配到消息时保留原信息。

spring:
  messages:
    # 消息文件基名,同时也是参数校验消息的基名
    basename: i18n/messages
    encoding: UTF-8

continew-starter:
  web:
    response:
      i18n: true
0=操作成功
1=服务器异常,请联系管理员

语言环境由应用配置的 LocaleResolver 决定,默认使用 AcceptHeaderLocaleResolver(读取 Accept-Language)。参数校验消息、业务异常消息同样通过该 MessageSource 与 LocaleContextHolder 解析,不需要 Starter 额外适配。

Query 参数转换器

默认情况下,Spring MVC 无法直接转换 Query 中的日期类型参数,需要使用 @DateTimeFormat 注解。

模块内置了 Query 参数转换器(WebMvcAutoConfiguration#addFormatters),可自动转换以下类型。

  • java.util.Date(经 Hutool DateUtil.parse 解析,支持多种常见日期格式)
  • LocalDateTime(默认格式 yyyy-MM-dd HH:mm:ss)
  • LocalDate(默认格式 yyyy-MM-dd)
  • LocalTime(默认格式 HH:mm:ss)
  • BaseEnum(枚举参数自动按编码值匹配)

如果默认格式无法满足要求,仍可使用 @DateTimeFormat 注解指定格式。

Tomcat 服务器增强

模块使用 Spring Boot 默认的 Tomcat 服务器(依赖 spring-boot-starter-webmvc),并做了两项增强。

全局禁用不安全的 HTTP 方法

配置项对应类为 top.continew.starter.web.autoconfigure.server.ServerExtensionProperties(配置前缀为 server.extension,非 continew-starter 前缀)。

server:
  extension:
    # 是否启用(默认:true)
    enabled: true
    # 不允许的请求方式(默认禁用 CONNECT、TRACE、TRACK)
    disallowed-methods:
      - CONNECT
      - TRACE
      - TRACK

Tomcat 线程池默认配置

模块内置了 Tomcat 的部分默认配置(见 continew-starter-web/src/main/resources/default-server.yml),可按需在 application.yml 中覆盖。

server:
  tomcat:
    # HTTP POST 请求内容大小上限(-1 不限制)
    max-http-form-post-size: -1
    threads:
      # 最大工作线程数
      max: 256

「全局禁用不安全的 HTTP 方法」由 Servlet 过滤器(DisallowedMethodsFilter)实现,不依赖具体容器,Tomcat、Jetty 等 Servlet 容器均生效。

核心依赖

依赖描述
top.continew.starter:continew-starter-json-jacksonJSON 模块(Jackson)
top.continew.starter:continew-starter-api-docAPI 文档模块
org.springframework.boot:spring-boot-starter-webmvcSpring MVC Web 开发能力(内置 Tomcat 服务器)

目录