SpringBoot3 WebFlux 拦截器 和 全局异常处理器

SpringBoot3 WebFlux 拦截器 和 全局异常处理器

一、拦截器

org.springframework.web.server.CoWebFilter其父类是org.springframework.web.server.WebFilter
webflux 的 拦截器 就像 ServletFilter 较为原始. 所以Filter咋使用。WebFilter就咋使用。而CoWebFilter是协程环境的Kotlin专用WebFilter,其内部使用mono将Mono转换为了kotlin的挂起函数。
CoWebFilter.kt

abstract class CoWebFilter : WebFilter {

	final override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
		val context = exchange.attributes[COROUTINE_CONTEXT_ATTRIBUTE] as CoroutineContext?
		return mono(context ?: Dispatchers.Unconfined) {
			filter(exchange, object : CoWebFilterChain {
				override suspend fun filter(exchange: ServerWebExchange) {
					exchange.attributes[COROUTINE_CONTEXT_ATTRIBUTE] = currentCoroutineContext().minusKey(Job.Key)
					chain.filter(exchange).awaitSingleOrNull()
				}
			})}.then()
	}

	/**
	 * Process the Web request and (optionally) delegate to the next
	 * [WebFilter] through the given [WebFilterChain].
	 * @param exchange the current server exchange
	 * @param chain provides a way to delegate to the next filter
	 */
	protected abstract suspend fun filter(exchange: ServerWebExchange, chain: CoWebFilterChain)

	companion object {

		/**
		 * Name of the [ServerWebExchange] attribute that contains the
		 * [kotlin.coroutines.CoroutineContext] to be passed to the
		 * [org.springframework.web.reactive.result.method.InvocableHandlerMethod].
		 */
		@JvmField
		val COROUTINE_CONTEXT_ATTRIBUTE = CoWebFilter::class.java.getName() + ".context"
	}

}

/**
 * Kotlin-specific adaption of [WebFilterChain] that allows for coroutines.
 *
 * @author Arjen Poutsma
 * @since 6.0.5
 */
interface CoWebFilterChain {

	/**
	 * Delegate to the next [WebFilter] in the chain.
	 * @param exchange the current server exchange
	 */
	suspend fun filter(exchange: ServerWebExchange)

}

源码分析:
代码非常简单哈,使用CoWebFilter只需要集成并且实现filter方法即可。

放行

放行只需要调用chain.filter(exchange)即可。并且其可以传递CoroutineContext上下文对象,类似于ThreadLocal.

拦截

val defaultMessage = "token is invalid"
exchange.response.apply {
    statusCode = HttpStatus.UNAUTHORIZED
    headers.contentType = MediaType.APPLICATION_JSON
    val result: edu.tyut.spring_boot_ssm.bean.Result<Boolean> = edu.tyut.spring_boot_ssm.bean.Result.failure(message = defaultMessage, data = false)
    val resultJson: String = objectMapper.writeValueAsString(result)
    val buffer: DataBuffer = bufferFactory().wrap(resultJson.toByteArray(charset = Charsets.UTF_8))
    writeWith(Mono.just<DataBuffer>(buffer)).awaitSingleOrNull()
}.setComplete().awaitSingleOrNull()

拦截也是比较简单的的直接在filter方法中调用以上方法即可

work

实现了filter方法,还不会生效。必须满足以下两个条件才可以生效

  1. webflux环境
  2. 注入bean容器

异常处理器

org.springframework.web.server.CoWebExceptionHandler#CoWebExceptionHandler
这个类是github社区的贡献的。

使用

  1. 实现CoWebExceptionHandler
  2. 重写coHandle方法
  3. 注入容器
  4. 提高优先级

案例

GlobalExceptionHandler.kt

@Order(value = -2)
@Component
internal final class GlobalExceptionHandler(
) : CoWebExceptionHandler() {

    private final val logger: Logger = LoggerFactory.getLogger(this.javaClass)

    override suspend fun coHandle(exchange: ServerWebExchange, ex: Throwable) {
        when (ex) {
            is WebExchangeBindException -> {
                this.handlerBindException(exchange = exchange, ex = ex)
            }
            else -> {
                this.handlerOtherException(exchange = exchange, ex = ex)
            }
        }
    }
}

TODO

为什么在webflux环境下,get请求的@RequestParam,可以生效而,post却不可以呢?

jakarta.validation.Constraint注解工作原理,并且针对Kotlin进行优化

SpringBoot 集成 Kotlin htmlx

WebFilter 源码解析

package org.springframework.web.server;

import reactor.core.publisher.Mono;

/**
 * 用于拦截式、链式处理Web请求的契约,可用于实现横切关注点、与应用无关的需求,
 * 如安全性、超时处理等。
 * Contract for interception-style, chained processing of Web requests that may
 * be used to implement cross-cutting, application-agnostic requirements such
 * as security, timeouts, and others.
 *
 * <p>Consider using {@code org.springframework.web.server.CoWebFilter} with
 * Kotlin Coroutines.
 *
 * @author Rossen Stoyanchev
 * @since 5.0
 */
public interface WebFilter {

	/**
	 * Process the Web request and (optionally) delegate to the next
	 * {@code WebFilter} through the given {@link WebFilterChain}.
	 * @param exchange the current server exchange
	 * @param chain provides a way to delegate to the next filter
	 * @return {@code Mono<Void>} to indicate when request processing is complete
	 */
	Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain);

}

WebExceptionHandler 源码解析

CoExchangeFilterFunction 与 HandlerFilterFunction -> CoRouterFunctionDsl.kt 代替的关系

posted @ 2025-06-09 00:21  爱情丶眨眼而去  阅读(146)  评论(0)    收藏  举报