You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Kotlin的Spring WebFlux响应式异常处理器实现遇错求助

Spring WebFlux 异常处理器实现修正方案

问题背景

参照Spring官方文档编写Spring WebFlux异常处理器时出现错误,原始代码如下:

import org.springframework.boot.autoconfigure.web.WebProperties
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler
import org.springframework.boot.web.reactive.error.ErrorAttributes
import org.springframework.context.ApplicationContext
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.RouterFunction
import org.springframework.web.reactive.function.server.RouterFunctions
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

@Component
class MyErrorWebExceptionHandler(errorAttributes: ErrorAttributes?, resources: WebProperties.Resources?,
    applicationContext: ApplicationContext?) : AbstractErrorWebExceptionHandler(errorAttributes, resources, applicationContext) {

    override fun getRoutingFunction(errorAttributes: ErrorAttributes): RouterFunction<ServerResponse> {
        return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml)
    }

    private fun acceptsXml(request: ServerRequest): Boolean {
        return request.headers().accept().contains(MediaType.APPLICATION_XML)
    }

    fun handleErrorAsXml(request: ServerRequest?): Mono<ServerResponse> {
        val builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
        // ... additional builder calls
        return builder.build()
    }

}

核心修正点

  • 构造函数参数与初始化修正
    Spring Boot 2.6+版本中,WebProperties.Resources需通过WebProperties实例获取,且构造参数应使用非空类型(Spring依赖注入会确保实例存在)。同时必须配置消息读写器,避免序列化异常:

    constructor(
        errorAttributes: ErrorAttributes,
        webProperties: WebProperties,
        applicationContext: ApplicationContext
    ) : super(errorAttributes, webProperties.resources, applicationContext) {
        setMessageWriters(ServerCodecConfigurer.create().writers)
        setMessageReaders(ServerCodecConfigurer.create().readers)
    }
    
  • 处理方法参数与逻辑完善
    handleErrorAsXml的ServerRequest参数不应为可空类型,且需从ErrorAttributes中获取异常详情生成有意义的响应:

    private fun handleErrorAsXml(request: ServerRequest): Mono<ServerResponse> {
        val error = getError(request)
        val status = determineStatus(error) ?: HttpStatus.INTERNAL_SERVER_ERROR
        
        val xmlResponse = """
            <error>
                <status>${status.value()}</status>
                <error>${status.reasonPhrase}</error>
                <message>${error.message ?: "Unknown error"}</message>
            </error>
        """.trimIndent()
        
        return ServerResponse.status(status)
            .contentType(MediaType.APPLICATION_XML)
            .bodyValue(xmlResponse)
    }
    
  • 覆盖多响应场景
    建议添加默认JSON响应处理,覆盖所有请求格式:

    override fun getRoutingFunction(errorAttributes: ErrorAttributes): RouterFunction<ServerResponse> {
        return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml)
            .andRoute(RequestPredicates.all(), this::handleErrorAsJson)
    }
    
    private fun handleErrorAsJson(request: ServerRequest): Mono<ServerResponse> {
        val error = getError(request)
        val status = determineStatus(error) ?: HttpStatus.INTERNAL_SERVER_ERROR
        
        val errorDetails = mapOf(
            "status" to status.value(),
            "error" to status.reasonPhrase,
            "message" to (error.message ?: "Unknown error")
        )
        
        return ServerResponse.status(status)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(errorDetails)
    }
    

完整修正代码

import org.springframework.boot.autoconfigure.web.WebProperties
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler
import org.springframework.boot.web.reactive.error.ErrorAttributes
import org.springframework.context.ApplicationContext
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.RequestPredicates
import org.springframework.web.reactive.function.server.RouterFunction
import org.springframework.web.reactive.function.server.RouterFunctions
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.ServerCodecConfigurer
import reactor.core.publisher.Mono

@Component
class MyErrorWebExceptionHandler(
    errorAttributes: ErrorAttributes,
    webProperties: WebProperties,
    applicationContext: ApplicationContext
) : AbstractErrorWebExceptionHandler(errorAttributes, webProperties.resources, applicationContext) {

    init {
        setMessageWriters(ServerCodecConfigurer.create().writers)
        setMessageReaders(ServerCodecConfigurer.create().readers)
    }

    override fun getRoutingFunction(errorAttributes: ErrorAttributes): RouterFunction<ServerResponse> {
        return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml)
            .andRoute(RequestPredicates.all(), this::handleErrorAsJson)
    }

    private fun acceptsXml(request: ServerRequest): Boolean {
        return request.headers().accept().contains(MediaType.APPLICATION_XML)
    }

    private fun handleErrorAsXml(request: ServerRequest): Mono<ServerResponse> {
        val error = getError(request)
        val status = determineStatus(error) ?: HttpStatus.INTERNAL_SERVER_ERROR
        
        val xmlResponse = """
            <error>
                <status>${status.value()}</status>
                <error>${status.reasonPhrase}</error>
                <message>${error.message ?: "Unknown error"}</message>
            </error>
        """.trimIndent()
        
        return ServerResponse.status(status)
            .contentType(MediaType.APPLICATION_XML)
            .bodyValue(xmlResponse)
    }

    private fun handleErrorAsJson(request: ServerRequest): Mono<ServerResponse> {
        val error = getError(request)
        val status = determineStatus(error) ?: HttpStatus.INTERNAL_SERVER_ERROR
        
        val errorDetails = mapOf(
            "status" to status.value(),
            "error" to status.reasonPhrase,
            "message" to (error.message ?: "Unknown error")
        )
        
        return ServerResponse.status(status)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(errorDetails)
    }
}

内容的提问来源于stack exchange,提问作者Saher Al-Sous

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 14:57:44