Skip to content

介绍

Web 模块,也是快速创建一个 Spring Boot Web 应用的可靠基石,主要特性包括

  • api接口版本:通过注解方式配置接口的版本
  • 跨域配置
  • 全局异常、错误处理器配置
  • 统一的long转字符串操作
  • 接口签名,接口请求参数解密和响应结果加密
  • 数据脱敏
  • 众多的 Validator,如生日,英文,身份证等
  • xss 攻击处理
  • sse 和 websocket服务

等等

依赖库

名称描述
hutool
knife4j-openapi2-spring-boot-starter
spring-boot-starter-web
spring-boot-starter-validation
spring-boot-starter-aop
zebra-spring-boot-common
guava
caffeine
spring-boot-starter-websocket
spring-tx
mapstruct-plus-spring-boot-starter

快速开始

依赖引入

xml

<parent>
	<groupId>io.github.zhanghongbin</groupId>
	<artifactId>zebra-spring-boot-parent</artifactId>
	<version>${version}</version>
</parent>

<dependencies>
 <dependency>
	<groupId>io.github.zhanghongbin</groupId>
	<artifactId>zebra-spring-boot-starter-web</artifactId>
 </dependency>
</dependencies>

在 @SpringBootApplication注解上边增加@EnableZebra注解

java

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

常用配置

1. 跨域配置 默认是启用跨域配置,如果想禁用采用以下方式配置

yml
zebra:
  web:
    cors:
	  enabled: false

2. swagger 配置 默认是启用 swagger 配置,如果想禁用采用以下方式配置

yml
zebra:
  web:
    swagger:
	  enabled: false

3. xss 配置 默认是启用 xss 配置,如果想禁用设置 enabled 为 false, excludes 是排除指定的uri,多个用,分割,示例: /usr/**,/usr

yml
zebra:
  web:
    xss:
	  enabled: false
	  excludes:

4. 全局响应拦截白名单配置 框架对返回体做了全局的拦截包装处理,如果不想进行拦截,需要配置白名单。 以下为白名单配置,多个uri前缀用,分割

yml
zebra:
  web:
    global-advice:
	  excludes: /app/**,/test1/**,/test2/**

状态码

0到-20为框架内使用

状态码描述
0成功
-1未知错误
-2参数校验失败
-3API接口已弃用
-4请求未找到
-5重复请求,请稍后重试
-6无效签名
-7数据库操作失败
-8解密失败
-9访问过于频繁,请稍后再试
-10未登录
-11登录超时
-12没有访问权限,请联系管理员授权
-13短信发送失败,请稍后再试

状态码是 StatusCode 枚举类,在zebra-spring-boot-common模块里,放在这里介绍是为了能够融入贯通。

异常处理

提供全局异常处理对于使用者来说无感知,包括:

  • 请求未找到异常 NoHandlerFoundException
  • 参数验证异常 BindException ValidationException MethodArgumentNotValidException BindException
  • 数据库操作异常 DataAccessException
  • 自定义异常 ResultException
  • 未知异常 Throwable

参数验证异常将异常提示转换成友好的RESTful响应 错误响应示例如下:

json
{
	"code": -3,
	"msg": "参数校验失败",
	"data": [
		{
			"errorKey": "userName",
			"errorMsg": "userName is null !"
		},
		{
			"errorKey": "userId",
			"errorMsg": "userId is null !"
		},
		{
			"errorKey": "age",
			"errorMsg": "age is null !"
		}
	]
}

自定义异常使用示例: 1. 使用R类的静态方法 R.failed() 默认状态码为-1 2. 使用R类的静态方法 R.failed(String msg) 可以定制msg 3. 使用R类的静态方法 R.failed(FailCode failCode) FailCode接口定义了状态码和描述信息,使用者可以根据业务需求定制异常枚举。

java
public enum MyFailCode implements FailCode {

		LOGIN_FAIL(-10001, "业务码必须唯一"),
		PASSWORD_FAIL(-10002, "无此业务码");
		private int code;
		private String msg;

		MyFailCode(int code, String msg) {
				this.code = code;
				this.msg = msg;
		}

		@Override
		public int getCode() {
				return code;
		}

		@Override
		public String getMsg() {
				return msg;
		}

		public static void main(String[] args) {
				//使用
			R.failed(MyFailCode.LOGIN_FAIL);
		}

}

4. 使用 ExceptionPlusUtil 类此类继承了hutool的ExceptionUtil类,提供了一些额外的方法。

静态方法描述
wrapResultException(String message)异常描述
wrapResultException(FailCode failCode)异常枚举

响应处理

响应体参数介绍

名称类型是否必须描述
code整型状态码
msg字符串描述
data对象返回值

响应示例:

json
{
	"code": 0,
	"msg": "ok",
	"data": ""
}

使用者不需要自行封装和处理响应体,框架实现了对有响应结果和无响应结果的处理,只需要返回结果数据即可。
示例:

java
@RequestMapping("/example")
public class ExampleController {

	@GetMapping("/test")
	public void test(@RequestParam("id") Long id) {
	}
}

返回结果为

json
{
	"code": 0,
	"msg": "成功",
	"data": true
}

如果不想框架对响应进行拦截和封装可以使用 @ExcludeResponse 注解来排除此接口。

枚举参数化及响应

请求方法参数支持枚举类型,需要有getValue()方法。
响应内容 1. 返回枚举所有字段 需要在枚举类上增加 @JsonFormat(shape = JsonFormat.Shape.OBJECT)注解 2. 返回枚举单一字段 需要字段上增加 @JsonValue

java

@Getter
public enum TestEnum {

		UNKNOWN(0, "未知"),
		MALE(1, "男");

		TestEnum(Integer value, String description) {
				this.value = value;
				this.description = description;
		}

		private Integer value;

		@JsonValue
		private String description;

		public Integer getValue() {
			return value;
		}
}

@RestController
@RequestMapping("/example")
public class ExampleController {

	@GetMapping
	public Long delete(@RequestParam("testNum") TestEnum testNum) {
			return 0L;
	}
}

访问地址为 /example/v1.0.0?testNum=1

api版本控制

通过注解 @ApiVersion 注解可优雅的实现接口版本控制,注解定义如下:

java

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ApiVersion {

		String value() default "1.0.0";

		/**
		 * 是否废弃版本接口
		 */
		boolean deprecated() default false;

		/**
		 * api类型分别有 pass 代表平台服务 lan 代表内网服务 open 开发给第三方服务
		 * @return
		 */
		ApiType type() default ApiType.EMPTY;


}

@ApiVersion 可以使用在类上,所有接口都会采用版本号,也可以只使用在方法上只有此接口采用版本号,当方法和类同时存在时,方法上的版本号会覆盖类上的版本号,方法上的@ApiVersion 注解优先级高, deprecated 参数为true此接口不能为访问,type 为url顶级前缀标识,会在url地址最前边增加 /pass,/lan 或 /open。 默认情况下版本号为放到类@RequestMapping地址后,当@RequestMapping路径有{version}占位符号,会采用占位符号的路径。
使用示例1:

java

@RestController
@RequestMapping("/basic")
@ApiVersion
public class ExampleController {

		@GetMapping("/user")
		public String get(@RequestParam("ids") List<String> idList) {
				return "333";
		}

}

访问地址为 /basic/v1.0.0/user?ids=1,2

使用示例2:

java

@RestController
@RequestMapping("/basic")
@ApiVersion
public class ExampleController {

		@GetMapping("/get")
		public UserInfoView get(@RequestParam("id") @NotNull Long id) {
				return UserInfoView.builder().id(id).name("name").build();
		}


		@GetMapping("/user")
		@ApiVersion(value = "1.0.1")
		public String get(@RequestParam("ids") List<String> idList) {
				return "333";
		}

}

访问地址为/basic/v1.0.0/get?id=1/basic/v1.0.1/user?ids=1,2

使用示例3:

java

@RestController
@RequestMapping("/basic/{version}/user")
@ApiVersion
public class ExampleController {

		@GetMapping
		@ApiVersion(value = "1.0.1")
		public String get(@RequestParam("ids") List<String> idList) {
				return "333";
		}

}

版本号会采用占位符号的位置 访问地址为 /basic/v1.0.1/user?ids=1,2

接口签名

使用@CheckSign注解对接口进行安全访问控制。
application.yml配置,secret-key为签名密钥,需要告知调用者,如果不进行配置默认secret-key为zebra-apisign-1234567890

yaml
zebra:
  web:
    sign:
	  secret-key: 123456

以下分别是get请求和post提交json内容请求

java

@RequestMapping("/example")
public class ExampleController {

		@GetMapping("/get")
		@CheckSign
		public String get(@RequestParam("id") @NotNull Long id) {
				return String.valueOf(id);
		}

		@PostMapping("/post")
		@CheckSign
		public String get(@RequestBody User user, @RequestParam("id") @NotNull long id) {
				System.out.println(user.getName());
				System.out.println(id);
				return "test";
		}
}

使用示例:(如果使用zebra框架,则不需要引入以下仓库)

xml

<dependency>
		<groupId>io.github.zhanghongbin</groupId>
		<artifactId>zebra-spring-boot-common</artifactId>
</dependency>

SignUtil.addSignParamsAndJoin 方法用来生成签名参数,第一个参数为密钥,第二个参数为请求参数,所有签名参数都需要放到url地址后边

java
public class ExampleTest {

		public static void testGetRequest() {
				Map<String, Object> aa = new HashMap<>();
				aa.put("id", 123);
				String params = SignUtil.addSignParamsAndJoin("zebra-apisign-1234567890", aa);
				String content = HttpUtil.get("http://localhost:9090/example/get?id=123&" + params);
				System.out.println(content);
		}

		public static void testPostJson() throws Exception {
				Map<String, Object> params = new HashMap<>();
				params.put("name", "xxx");
				params.put("age", 1);
				params.put("id", 123);
				String s = SignUtil.addSignParamsAndJoin("zebra-apisign-1234567890", params);
				params.remove("id");
				String body = HttpRequest.post("http://localhost:9090/example/post?id=123&" + s)
								.body(JSONUtil.toJsonStr(params))
								.execute()
								.body();
				System.out.println(body);
		}

		public static void main(String[] args) throws Exception {
				testPostJson();
		}
}

feign接口签名示例:
参数 signCheck 为签名值,可以调用SignUtil.addSignParams();方法返回签名数据,数据类型为Map

java

@FeignClient(
				name = "ExampleServiceStub",
				url = "${rpc.url}"
)
public interface ExampleServiceStub {
		@GetMapping("/example/v1.0.1/get")
		UserInfoView get(@RequestParam("id") Long id, @RequestParam Map signCheck);
}

数据脱敏

使用@Sensitive对自动进行注解,技术实现使用hutool进行支持,分别提供以下脱敏方式

  • 身份证脱敏
  • 手机号脱敏
  • 地址脱敏
  • 邮箱脱敏
  • 银行卡
  • 用户姓名

使用 @Sensitive 注解到类字段上,注解参数可以为以下枚举参数

java

@AllArgsConstructor
@Getter
public enum SensitiveStrategy {

		/**
		 * 身份证脱敏
		 */
		ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)),

		/**
		 * 手机号脱敏
		 */
		PHONE(DesensitizedUtil::mobilePhone),

		/**
		 * 地址脱敏
		 */
		ADDRESS(s -> DesensitizedUtil.address(s, 8)),

		/**
		 * 邮箱脱敏
		 */
		EMAIL(DesensitizedUtil::email),

		/**
		 * 银行卡
		 */
		BANK_CARD(DesensitizedUtil::bankCard),

		/**
		 * 用户姓名
		 */
		USER_NAME(SensitiveStrategy::userName);

		private final Function<String, String> desensitizer;

		private static String userName(String userName) {
				//return StringUtils.rightPad(StringUtils.left(userName, 1), userName.length(),"*");
				return StrUtil.hide(userName, 1, userName.length());
		}


}

如果想动态控制是否进行脱敏,需要实现 Condition 接口,并设置到 @Sensitive 注解的 condition 参数上。
其次还支持 spel 表达式注解 @SpelSensitive value属性编写el表达式,一般情况下是调用其它的服务来控制脱敏规则,示例:

java
//调用服务名称为abc的 test 方法,#versionName 为变量,名称必须和versionName一致,才能获取到 versionName 的值
@SpelSensitive(value = "#{@abc.test(#versionName)}")
private String versionName;


@Service("abc")
public class AbcService {

		public Integer test(String u) {
				return 1;
		}
}

接口加解密

如果想保证数据传输的安全,对接口出参加密,入参解密,目前提供 AES、RSA,SM2 加解密配置 并使用 org.zebra.common.crypto 包下的 AesCryptoProvider,RsaCryptoProvider,Sm2CryptoProvider类

第一步:生成密钥,或公钥私钥,以aes加解密为例:

java
AesCryptoProvider.generateSecretKey();

RsaCryptoProvider 和 Sm2CryptoProvider分别提供静态方法生成无证书的公钥和私钥
第二步:把生成的密钥配置到服务器端的yaml文件中

yml
zebra:
  web:
    crypto:
	  algorithm: aes
	  secret-key: 212341231
	  public-key:
	  private-key:
字段描述
algorithm配置算法:aes,rsa,sm2
secret-keyaes 算法需要配置密钥
public-keyrsa和sm2需要配置公钥,公钥用来加密
private-keyrsa和sm2需要配置私钥,私钥用来解密

第三步:
使用 @ApiCrypto 注解在接口上进行标识,@ApiCrypto 有两个属性分别为 request,response 代表是否对请求进行解密,是否对响应进行加密 默认都为true,示例:

java

@RestController
@RequestMapping("/test")
public class UserTest2Controller {

	@PostMapping(value = "/c")
	@ApiCrypto
	public Long b(@RequestBody Map<String, Object> a) {
		return 1l;
	}
}

第四步:如果调用者引入了zebra-spring-boot-web-starter,则直接可以使用 @Autowired CryptoProvider 类,否则需要映入以下依赖

xml
<dependency>
		<groupId>org.zebra</groupId>
		<artifactId>zebra-spring-boot-common</artifactId>
		<version></version>
</dependency>

使用org.zebra.common.crypto包下的 AesCryptoProvider,RsaCryptoProvider,Sm2CryptoProvider 进行加解密操作,示例:

java
public static void main(String[] args) throws Exception {
		Map<String, String> aa = new HashMap<>();
		aa.put("name", "rfff");
		String json = JSONUtil.toJsonStr(aa);
		AesCryptoProvider aesSecurityProvider = new AesCryptoProvider("dfQIa54eVbIyP3A3QXBxoQ==");
		String content = aesSecurityProvider.encrypt(json);
		String result = HttpRequest.post("http://localhost:9090/zhn/c")
						.header("Content-Type", "application/json")
						.body(content, "application/json")
						.execute()
						.body();
		System.out.println(aesSecurityProvider.decrypt(JSONUtil.parseObj(result).getStr("data")));
}

以上使用hutool 提供的http请求进行访问接口,返回加密结果如下:

json
{
	"code": 0,
	"msg": "成功",
	"data": "erLgS7uxWu96/YiRUAWHNg=="
}

sse 服务

SSE(Server Sent Event) ,是一种可以主动从服务端推送消息的技术。SSE的本质其实就是一个HTTP的长连接,只不过它给客户端发送的不是一次性的数据包,而是一个stream流,格式为text/event-stream。所以客户端不会关闭连接,会一直等着服务器发过来的新的数据流。 SSE连接超时,当SSE客户端连接后,如果长时间不断开,它会保持连接状态。SSE(Server-Sent Events)是一种基于HTTP的推送技术,允许服务器实时向客户端发送数据。当客户端连接到服务器的SSE端点时,它会创建一个EventSource对象,该对象将与服务器进行长期连接,并接收服务器发送的事件。只有在客户端手动关闭连接或连接发生错误时,才会断开SSE连接 在使用SSE的客户端连接中,如果长时间不断开连接,可能会出现以下情况:

1. 连接超时。如果服务器端没有发送新的事件数据,而客户端也没有重新建立连接,可能会超过服务器的连接超时时间。这可能导致服务器关闭连接,客户端需要重新建立连接才能接收新的事件数据。 2. 网络异常。长时间不断开连接可能会导致网络异常,例如连接中断、丢包等问题。在这种情况下,客户端可能需要重新建立连接,以恢复和服务器的通信。

假设服务端设置为30s超时连接,在客户端连接服务端后开始计时,如果在这30s内,服务端有向该客户端发送数据,那么在30s时间到了之后,服务端会先断开客户端的连接然后重新连接客户端,并开始新一轮计时;如果在这30s内,服务端没有向客户端发送数据,那么30s后服务端会断开客户端的连接,不再重连。 首先需要开启 sse 服务,默认 enabled 为 false

yml
zebra:
  web:
    sse:
	  enabled: true

前端使用 可以连接sse服务,sse服务提供以下两个接口:

接口地址描述
Get 请求 /sse/subscribe/用于前端和sse服务连接,id为客户端标识
Get 请求 /sse/close/用于前端和sse服务关闭连接,id为客户端标识

后端使用 首先在前端连接到sse服务的时候,会发生连接事件,后端可以使用此事件做一些业务处理,示例如下: SseEmitterService 对象在应用启动的时候就注入到spring容器中,可以拿来使用,此事件是通过spring发送的异步事件, 事件对象 SseConnectionEvent 可以获取客户端id

java
@Component
public class SseConnectionListener {

		@Autowired
		private SseEmitterService sseEmitterService;

		@Async
		@EventListener
		public void connectEvent(SseConnectionEvent sseConnectionEvent) {
				SseMessage<String> aa = new SseMessage<>("xxxxx");
				sseEmitterService.send(sseConnectionEvent.getId(), aa);
		}
}

SseAuthHandler 接口
为后端使用者提供了认证功能,如果需要自定义认证逻辑,可以实现 SseAuthHandler 接口,作为spring的bean注入到容器中。示例:

java
@Component
public class SaTokenSseAuthHandler implements SseAuthHandler {

    @Override
    public boolean auth(String id) {
        return StpUtil.isLogin();
    }
}

SseEmitterService 对象
为后端使用者提供了以下方法用来把消息发送到前端

方法名称描述
public boolean send(String id, SseMessage<?> message)发送消息到指定客户端
public void send(SseMessage<?> message)给所有连接的客户端发送消息
public void send(Set<String> ids, SseMessage<?> message)给多个客户端发送消息
public Set<String> getAllSseId()获取所有客户端id,不能保证连接有效
public SseEmitterUTF8 getSseEmitter(String id)根据客户端id获取sse连接对象

框架为后端提供了前端测试页面,只是为了测试,不代表真实的使用场景。 使用示例: 因为需要跳转到页面,需要增加以下依赖

xml
<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

增加@Controller注解并使用 @ExcludeResponse 注解 index 方法使框架不进行拦截,转向sse.html页面, 模拟发送消息的需要指定@ResponseBody这样给模拟调用者输出json格式。

java

@Controller
@RequestMapping("/mock")
public class UserTest1Controller {

		@Autowired
		private SseEmitterService sseEmitterService;

		@GetMapping("/index")
		@ExcludeResponse
		public String index() {
				return "sse";
		}

		@GetMapping("/send")
		@ResponseBody
		public void send() {
				SseMessage<String> aa = new SseMessage<>("ffff");
				sseEmitterService.send("1", aa);

		}
}

访问地址为: http://localhost:端口号/mock/index 注意: 一般情况下后端不会把所有数据一起发送给前端,而是会把页面分成多个模块,然后发给前端,此时前端需要区分哪一块数据对应哪一块页面。所以我们可以给各个模块的数据起个名字。也就是以下的代码 后端在构造SseMessage 的时候可以指定topic , new SseMessage(String topic, T data) 而前端需要设置

js
eventSource.addEventListener("topic", function (event) {
console.log(event.data);
.....
});

websocket 服务

默认情况下是禁用 websocket ,如果启用配置如下:

yml
zebra:
  web:
    websocket:
	  enabled: true

1. 接收 websocket 消息

需要实现 WebSocketMessageListener 接口,此接口为泛型接口,onMessage 方法为接收的消息事件,getType 方法用来注册消息类型,前端发送需要指定消息类型和消息内容,示例:

json
{
	"type": "test",
	"content": {
		"id": 1,
		"name": "测试"
	}
}

json 格式 为 type 和 content两个属性 content 值由发送方和接收方协调一致。

java
@Component
public class Test implements WebSocketMessageListener<UserLog> {

		@Override
		public void onMessage(WebSocketSession session, UserLog message) {

		}

		@Override
		public String getType() {
				return "test";
		}
}

2. 发送 websocket 消息

TokenHandler 接口是获取登录信息并和 websocket session 进行关联,发送消息的时候会使用 type 或 id 来进行指定, type 是组的概念,可以以组进行发送消息。 默认实现为

java
public final class DefaultTokenHandler implements TokenHandler {
    @Override
    public LoginInfo getLoginInfo(HttpServletRequest httpServletRequest) {
        String token = httpServletRequest.getParameter("token");
        if (token == null) return null;
        LoginInfo loginInfo = new LoginInfo();
        loginInfo.setId(token);
        loginInfo.setType(token);
        return loginInfo;
    }
}

通常情况下使用者需要实现 TokenHandler 接口,并注入到 spring 容器中。 WebSocketEmitterService 对象提供了发送消息的方法,可以从 spring 环境中获取此对象

方法名称描述
send(String type, String id, String bizType, String messageContent)bizType 代表的为业务类型一般情况下是和 WebSocketMessageListener getBizType 方法返回值一致
send(String type, String bizType, String messageContent)给所有type 类型的用户发送消息
void sendAll(String bizType, String messageContent)给所有用户发送消息

使用示例:

java
@Component
public class L1 implements WebSocketMessageListener<UserLog> {

		@Resource
		private WebSocketEmitterService webSocketEmitterService;

		@Override
		public void onMessage(WebSocketSession session, UserLog message) {
				webSocketEmitterService.send("abc", "123456","l1", "rrrrrrr");
				System.out.println(message);
		}

		@Override
		public String getType() {
				return "l1";
		}
}

Validated校验

Validated 用于检查所有被注解字段的值是否符合预期 Bean Validation 中内置的 constraint

注解作用
@Null被注解的元素必须为 null
@NotBlank被注解的元素必须不为空,并且必须包含至少一个非空白字符
@NotEmpty被注解的元素必须非空
@AssertTrue被注解的元素必须为 true
@AssertFalse被注解的元素必须为 false
@Max被注解的元素必须是一个数字,其值必须小于等于指定的最大值
@Min被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax被注解的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@Digits被注解的元素必须是一个数字,其值必须在可接受的范围内
@Positive被注解的元素必须是严格意义上的正数
@PositiveOrZero被注解的元素必须是正数或0
@Negative被注解的元素必须是一个严格意义上的负数
@NegativeOrZero被注解的元素必须是负数或0
@Past被注解的元素必须是过去的某个瞬间、日期或时间
@PastOrPresent被注解的元素必须是过去或现在的某个瞬间、日期或时间
@Future被注解的元素必须是将来的某个瞬间、日期或时间
@FutureOrPresent被注解的元素必须是当前或将来的某个瞬间、日期或时间
@Size被注解的元素的大小必须在指定的范围内
@Email被注解的元素必须是电子邮箱地址
@Pattern被注解的元素必须符合指定的正则表达式

Hibernate Validator 附加的 constraint

注解作用
@Length被注解的字符串的大小必须在指定的范围内
@Range被注解的元素必须在合适的范围内
@URL验证带注解的字符串是否为URL
@Currency货币金额必须在正确的货币单位
@CreditCardNumber带注解的元素必须表示有效的信用卡号
@CodePointLength验证包含字符序列的代码点长度在min和max之间
@ConstraintComposition布尔运算符,应用于组合约束注解的所有约束
@SafeHtml验证用户提供的富文本值,以确保它不包含恶意代码
@UniqueElements验证所提供集合中的每个对象都是惟一的,即不能在集合中找到两个相等的元素
@EAN检查带注解的字符序列是否是有效的EAN 13号。验证数字的长度和校验数字
@ISBN检查带注解的字符序列是否是有效的ISBN。数字的长度和校验数字都经过验证
@LuhnCheckLuhn算法检查约束
@Mod10CheckModulo 10 检查约束
@Mod11CheckModulo 11 检查约束
@ParameterScriptAssert方法级约束,它根据带注解的方法或构造函数计算脚本表达式
@ScriptAssert类级约束,它根据带注解的元素计算脚本表达式

zebra Validator 附加的 constraint

注解作用
@Cellphone手机号校验
@Birthday生日校验
@Chinese中文校验
@CreditCode是否是有效的统一社会信用代码
@ZipCode验证是否为邮政编码(中国)
@IdCard身份证校验
@English英语校验
@DictPattern字典校验
@JsonPatternjson格式校验
@EnumPattern枚举校验

@DictPattern ( 版本>1.2.1)
dictType 属性为字典类型,validator 为 spring 管理的校验类,此类必须有 public String getDictLabel(String dictType, String dictValue, String separator) 方法

@JsonPattern ( 版本>1.2.1)
type 属性可以为:
OBJECT (JSON 对象,例如 {"a":1})
ARRAY (JSON 数组,例如 [1,2,3]) ANY (任意 JSON 类型,对象或数组都可以)

@EnumPattern ( 版本>1.2.1)
type 属性为枚举类,fieldName 属性为枚举类中的字段名,还需确保该字段实现了 getter 方法。

示例:

java
@Getter
@AllArgsConstructor
public enum LoginType {

    /**
     * 密码登录
     */
    PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"),

    /**
     * 短信登录
     */
    SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),

    /**
     * 登录重试超出限制提示
     */
    final String retryLimitExceed;

    /**
     * 登录重试限制计数提示
     */
    final String retryLimitCount;
}
@Data
public class Test {
  @EnumPattern(type = LoginType.class,fieldName = "retryLimitExceed")
  private String c;
}
  • @Valid注解由java提供,验证时不支持指定校验分组。 用在方法、构造函数、方法参数和成员属性(field)上
  • @Validated注解由spring扩展提供,验证时支持指定校验分组,推荐使用。 用在类型、方法和方法参数上。但不能用于成员属性(field)

参数如果是非对象格式,需要在controller类上面添加@Validated注解。

其它

  • 接口返回非json问题

有些情况下 Controller 方法中返回String类型,调用者本来应该获取到的格式为

json
{
	"code": 0,
	"msg": "",
	"data": ""
}

但确是data里的内容,这是因为spring里转换器的问题造成的,可以有两种方式解决此问题 第一种如下: 强制设置输出 produces = "application/json" json格式

java

@RestController
@RequestMapping("/zhn")
public class UserTest2Controller {
		@PostMapping(value = "/c", produces = "application/json")
		public Long b(@RequestBody Map<String, Object> a) {
				return 1l;
		}
}

第二种: 在客户端调用时候请求报头增加 Accept 为 application/json

  • 日期参数支持
java

@GetMapping("/getDate")
public Date get(@RequestParam(value = "date") Date date) {
		return new Date();
}
  • MapStructPlusUtil 类

在org.zebra.web.util包下提供 MapStructPlus 实用类