Kotlin 如何在SpringCloudGateway中编写用于检查请求正文的自定义GlobalFilter?

Kotlin 如何在SpringCloudGateway中编写用于检查请求正文的自定义GlobalFilter?,kotlin,spring-cloud,spring-cloud-gateway,Kotlin,Spring Cloud,Spring Cloud Gateway,我想验证GlobalFilter中的正文 我需要读取包含正文校验和的两个http头,并将其与正文本身进行比较: internal class MyFilter : GlobalFilter { override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain) = ByteArrayDecoder() .decodeToMono(

我想验证
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) }
}
问题是
decodingtomino
阻塞并且不转发请求


如何正确解码正文?

我已成功编写了一个在读取正文后不会卡住的过滤器:

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
            }
        }
    }
}
接口体过滤器{
趣味过滤器(
正文:Mono,
exchange:ServerWebExchange,
passRequestFunction:()->Mono
):Mono
}
类HeaderAndBodyGlobalFilter(私有值bodyFilter:bodyFilter):全局过滤器{
private val messageReaders:List=HandlerStrategies.withDefaults().messageReaders()
覆盖有趣的过滤器(exchange:ServerWebExchange,链:GatewayFilterChain):Mono{
val serverRequest:serverRequest=serverRequest.create(exchange、messageReaders)
val body:Mono=serverRequest.bodyToMono(ByteArrayResource::class.java)
返回bodyFilter.filter(body,exchange){重构请求(body,exchange,chain)}
}
私人娱乐重建请求(
正文:Mono,
exchange:ServerWebExchange,
链:网关过滤器链
):Mono{
val headers:HttpHeaders=writableHttpHeaders(exchange.request.headers)
val outputMessage=CachedBodyOutputMessage(交换,标题)
返回BodyInserters.fromPublisher(
身体,
ByteArrayResource::class.java
).insert(outputMessage,BodyInserterContext())
.然后(单声道){
val decorator:ServerHttpRequestDecorator=decoration(
交换、标题、输出消息
)
链条
.filter(exchange.mutate().request(decorator.build())
})
}
私人娱乐装饰(
exchange:ServerWebExchange,
标题:HttpHeaders,
outputMessage:CachedBodyOutputMessage
):ServerHttpRequestDecorator{
返回对象:ServerHttpRequestDecorator(exchange.request){
重写fun getHeaders():HttpHeaders{
val contentLength=headers.contentLength
val httpHeaders=httpHeaders()
httpHeaders.putAll(super.getHeaders())
如果(contentLength>0){
httpHeaders.contentLength=contentLength
}否则{
//TODO:这会导致在上出现“HTTP/1.1 411所需长度”//
//httpbin.org
httpHeaders.set(httpHeaders.TRANSFER_编码,“分块”)
}
返回HttpHeader
}
重写fun getBody():Flux{
返回outputMessage.body
}
}
}
}

然后实现
BodyFilter
在失败时返回
Mono.empty()
,或者在成功时调用
passRequestFunction

在过滤器中使用ModifyRequestBody过滤器?@Spencergib我还需要使用来自标题的信息(标题存储正文的校验和)-这就是为什么我认为
GlobalFilter
非常适合。无论如何,我将更新问题以反映该信息。从该筛选器复制then@spencergibb非常感谢,执行:)