kotlin - 如何编写自定义 GlobalFilter 以检查 Spring Cloud Gateway 中的请求正文?
问题描述
我想在GlobalFilter
.
我需要读取两个包含正文校验和的 http 标头,并将其与正文本身进行比较:
internal class MyFilter : GlobalFilter {
override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain) =
ByteArrayDecoder()
.decodeToMono(
exchange.request.body,
ResolvableType.forClass(ByteBuffer::class.java),
exchange.request.headers.contentType,
null
)
.flatMap { /* my logic checking body against request headers */ chain.filter(exchange) }
}
问题是decodingToMono
卡住并且不转发请求。
我怎样才能正确解码身体?
解决方案
我设法编写了一个在阅读正文后不会卡住的过滤器:
interface BodyFilter {
fun filter(
body: Mono<ByteArrayResource>,
exchange: ServerWebExchange,
passRequestFunction: () -> Mono<Void>
): Mono<Void>
}
class HeaderAndBodyGlobalFilter(private val bodyFilter: BodyFilter) : GlobalFilter {
private val messageReaders: List<HttpMessageReader<*>> = HandlerStrategies.withDefaults().messageReaders()
override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
val serverRequest: ServerRequest = ServerRequest.create(exchange, messageReaders)
val body: Mono<ByteArrayResource> = serverRequest.bodyToMono<ByteArrayResource>(ByteArrayResource::class.java)
return bodyFilter.filter(body, exchange) { reconstructRequest(body, exchange, chain) }
}
private fun reconstructRequest(
body: Mono<ByteArrayResource>,
exchange: ServerWebExchange,
chain: GatewayFilterChain
): Mono<Void> {
val headers: HttpHeaders = writableHttpHeaders(exchange.request.headers)
val outputMessage = CachedBodyOutputMessage(exchange, headers)
return BodyInserters.fromPublisher(
body,
ByteArrayResource::class.java
).insert(outputMessage, BodyInserterContext())
.then(Mono.defer {
val decorator: ServerHttpRequestDecorator = decorate(
exchange, headers, outputMessage
)
chain
.filter(exchange.mutate().request(decorator).build())
})
}
private fun decorate(
exchange: ServerWebExchange,
headers: HttpHeaders,
outputMessage: CachedBodyOutputMessage
): ServerHttpRequestDecorator {
return object : ServerHttpRequestDecorator(exchange.request) {
override fun getHeaders(): HttpHeaders {
val contentLength = headers.contentLength
val httpHeaders = HttpHeaders()
httpHeaders.putAll(super.getHeaders())
if (contentLength > 0) {
httpHeaders.contentLength = contentLength
} else {
// TODO: this causes a 'HTTP/1.1 411 Length Required' // on
// httpbin.org
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked")
}
return httpHeaders
}
override fun getBody(): Flux<DataBuffer> {
return outputMessage.body
}
}
}
}
然后执行失败BodyFilter
返回Mono.empty()
或调用passRequestFunction
成功。
推荐阅读
- vba - 将粘贴格式从 sheet1 复制到新的附加工作表
- react-native - wix 反应原生导航 v2 | 如何从侧抽屉组件将新屏幕推送到当前屏幕
- ios - com.apple.developer.healthkit.access 警告
- wordpress - 删除 Woocommerce 中已定义产品类别的结帐字段
- jenkins - 主动选择插件 Groovy 脚本 - 读取环境变量
- python-3.x - Python 类型提示填充了 myclass 对象的双端队列
- ios - 转换为 sksprite 节点
- java - 加快 Firebase 存储下载
- javascript - 无法打开安卓应用
- iphone - 如何快速制作带彩光的手电筒(iPhone)?