Play Framework 2.8如何实现请求前后拦截 替代Spring HandlerInterceptor功能
Play Framework 2.8 请求拦截(NDC上下文注入)实现方案
Play 框架中没有直接命名为HandlerInterceptor的组件,你的需求可以通过全局ActionCreator(Java推荐) 或 全局Filter(Scala推荐) 实现,逻辑完全对齐Spring的preHandle/postHandle能力,无需使用底层的HttpRequestHandler。
Java版本实现(适配Spring技术栈使用习惯)
1. 自定义全局ActionCreator
实现play.mvc.ActionCreator接口,在请求执行前后插入你的业务逻辑:
import play.mvc.Action; import play.mvc.Http; import play.mvc.Result; import play.mvc.ActionCreator; import org.slf4j.MDC; // 如果使用Log4j的ThreadContext,替换为 import org.apache.logging.log4j.ThreadContext; import java.lang.reflect.Method; import java.util.UUID; import java.util.concurrent.CompletionStage; public class NdcInjectActionCreator implements ActionCreator { private static final String NDC_SESSION_KEY = "request_ndc"; @Override public Action createAction(Http.Request request, Method actionMethod) { return new Action.Simple() { @Override public CompletionStage<Result> call(Http.Request req) { // preHandle逻辑:读取/生成NDC存入线程上下文 String ndc = req.session() .get(NDC_SESSION_KEY) .orElseGet(() -> UUID.randomUUID().toString().replace("-", "")); MDC.put(NDC_SESSION_KEY, ndc); // Log4j版本对应:ThreadContext.put(NDC_SESSION_KEY, ndc); // 新生成的NDC回存到session Http.Request reqWithNdc = req.addAttr( Http.Attrs.SESSION, req.session().adding(NDC_SESSION_KEY, ndc) ); try { // 执行原请求逻辑 return delegate.call(reqWithNdc); } finally { // postHandle逻辑:清理线程上下文,避免内存泄漏 MDC.remove(NDC_SESSION_KEY); // Log4j版本对应:ThreadContext.remove(NDC_SESSION_KEY); } } }; } }
2. 注册组件
在conf/application.conf中添加配置,让Play加载你的自定义ActionCreator:
play.http.actionCreator = "替换为你自己的包名.NdcInjectActionCreator"
Scala版本实现
1. 自定义全局Filter
import akka.stream.Materializer import play.api.mvc._ import org.slf4j.MDC import java.util.UUID import scala.concurrent.ExecutionContext import scala.util.Try class NdcInjectFilter(implicit val mat: Materializer, ec: ExecutionContext) extends Filter { private val NDC_SESSION_KEY = "request_ndc" override def apply(nextFilter: RequestHeader => Future[Result])(request: RequestHeader): Future[Result] = { // preHandle逻辑 val ndc = request.session.get(NDC_SESSION_KEY).getOrElse(UUID.randomUUID().toString.replace("-", "")) MDC.put(NDC_SESSION_KEY, ndc) val reqWithNdc = request.addAttr(Attrs.Session, request.session + (NDC_SESSION_KEY -> ndc)) val resultF = nextFilter(reqWithNdc) // postHandle逻辑,无论请求成功失败都清理上下文 resultF.onComplete(_ => Try(MDC.remove(NDC_SESSION_KEY))) resultF } }
2. 注册组件
在conf/application.conf中添加配置启用Filter:
play.filters.enabled += "替换为你自己的包名.NdcInjectFilter"
注意事项
- 如果你在请求中使用了自定义异步线程池,需要自行传递ThreadContext/MDC到子线程,避免上下文丢失
- 上述实现已经覆盖了异常场景的上下文清理,不会出现内存泄漏问题
内容的提问来源于stack exchange,提问作者Petre Popescu
相关产品推荐
相关产品推荐

