如何在Spring Cloud Gateway中校验请求体与指定Schema的匹配性?
Spring Cloud Gateway 仅转发符合指定JSON Schema的请求实现方案
核心思路
通过异步Predicate结合JSON Schema校验实现,同时利用Spring官方工具缓存请求体,避免后续转发时请求体丢失,逻辑集中且高效,无需复杂过滤器链。
步骤实现
1. 准备JSON Schema文件
在resources/schema下创建校验规则文件,例如example-schema.json:
{ "type": "object", "required": ["id", "name"], "properties": { "id": {"type": "integer"}, "name": {"type": "string", "minLength": 2} } }
2. 引入依赖
添加JSON Schema校验所需依赖到pom.xml:
<dependency> <groupId>org.everit.json</groupId> <artifactId>org.everit.json.schema</artifactId> <version>1.14.0</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20230618</version> </dependency>
3. 编写路由配置
修改你的路由函数,使用asyncPredicate完成异步校验:
import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.core.io.ClassPathResource; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.core.io.Resource; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.everit.json.schema.Schema; import org.everit.json.schema.loader.SchemaLoader; import org.json.JSONObject; import org.json.JSONTokener; import reactor.core.publisher.Mono; import java.io.InputStream; @Component public class ValidatedRouteLocator { private final Schema requestSchema; // 初始化JSON Schema校验规则 public ValidatedRouteLocator() throws Exception { Resource schemaResource = new ClassPathResource("schema/example-schema.json"); try (InputStream inputStream = schemaResource.getInputStream()) { JSONObject rawSchema = new JSONObject(new JSONTokener(inputStream)); requestSchema = SchemaLoader.load(rawSchema); } } public RouteLocator routes(RouteLocatorBuilder builder) { return builder.routes() .route("schema_validated_api", spec -> spec // 异步校验请求体是否符合Schema .asyncPredicate(exchange -> { // 跳过非JSON类型请求 MediaType contentType = exchange.getRequest().getHeaders().getContentType(); if (contentType == null || !contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) { return Mono.just(false); } // 缓存请求体,保证后续转发可读取 return ServerWebExchangeUtils.cacheRequestBody(exchange, cachedRequest -> cachedRequest.getBody() .aggregate() .map(this::convertDataBufferToJson) .map(this::validateAgainstSchema) ); }) .and() .path("/api/example") .uri("http://SOME_SERVICE") ) .build(); } // 将DataBuffer转换为JSON对象 private JSONObject convertDataBufferToJson(DataBuffer dataBuffer) { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); return new JSONObject(new String(bytes)); } // 校验JSON是否符合Schema private boolean validateAgainstSchema(JSONObject jsonObject) { try { requestSchema.validate(jsonObject); return true; } catch (Exception e) { // 校验失败,返回false,请求不会被转发 return false; } } }
关键说明
- 使用
asyncPredicate:适配反应式场景,支持异步校验逻辑,避免阻塞。 - 请求体缓存:通过
ServerWebExchangeUtils.cacheRequestBody处理请求体的重复读取问题,保证校验后转发时请求体可用。 - 提前过滤非JSON请求:减少无效校验操作,提升性能。
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

