如何捕获withSizeLimit指令抛出的EntityStreamSizeException
问题分析与解决方案
核心原因
你遇到的问题根源在于:EntityStreamSizeException 并不会直接以异常形式抛出到 handleException 的作用域中,而是被Akka HTTP自动转换为请求实体拒绝(RequestEntityRejection);同时你之前的指令顺序错误,handleRejection/handleException 放在了parameter指令内部,无法捕获到实体解析阶段触发的大小超限拒绝。
正确解决方案
步骤1:编写正确的自定义拒绝处理器
针对Akka HTTP的EntityStreamSizeExceededRejection(部分版本通过RequestEntityRejection包装)编写处理器,返回自定义的413 Payload Too Large响应:
import akka.http.scaladsl.server.RejectionHandler import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.rejections.{EntityStreamSizeExceededRejection, RequestEntityRejection} // 适配不同Akka HTTP版本的两种写法: // 写法1:直接匹配EntityStreamSizeExceededRejection(多数新版本) val customRejectionHandler: RejectionHandler = RejectionHandler.newBuilder() .handle { case EntityStreamSizeExceededRejection(limit, actualSize) => val limitMB = limit / (1024 * 1024) val actualMB = actualSize / (1024 * 1024) complete(StatusCodes.PayloadTooLarge, s"文件大小超过限制:最大允许${limitMB}MB,实际上传${actualMB}MB") } .result() // 写法2:匹配RequestEntityRejection包装的异常(部分旧版本) // val customRejectionHandler: RejectionHandler = RejectionHandler.newBuilder() // .handle { // case RequestEntityRejection(_, ex: EntityStreamSizeException) => // val limitMB = ex.limit / (1024 * 1024) // val actualMB = ex.actualSize / (1024 * 1024) // complete(StatusCodes.PayloadTooLarge, s"文件大小超过${limitMB}MB限制,实际上传${actualMB}MB") // } // .result()
步骤2:调整路由指令顺序
将handleRejection放在路由的最外层,确保它能覆盖到withSizeLimit和entity指令的执行范围:
final val MEDIA_FILE_UPLOAD_MAX_SIZE = 30 * 1024 * 1024 def uploadMediaObjectForASite(): Route = handleRejection(customRejectionHandler) { path("Some path") { userId => post { parameter(Symbol("location_id").as[String]) { locationId => withSizeLimit(MEDIA_FILE_UPLOAD_MAX_SIZE) { withRequestTimeout(120.seconds) { entity(as[Multipart.FormData]) { formData => val metadataList = List(TITLE, DESCRIPTION, UPLOADED_BY) // 执行文件上传逻辑 complete(StatusCodes.OK, "文件上传成功") } } } } } } }
可选:全局生效配置
如果希望所有路由都使用该自定义拒绝处理器,可以在启动HTTP服务器时配置全局默认处理器:
import akka.http.scaladsl.Http import akka.http.scaladsl.settings.HttpServerSettings val serverSettings = HttpServerSettings(system) .withDefaultRejectionHandler(customRejectionHandler) val bindingFuture = Http() .newServerAt("0.0.0.0", 8080) .withSettings(serverSettings) .bind(uploadMediaObjectForASite())
为什么之前的方法无效
handleException无效:Akka HTTP会将EntityStreamSizeException转换为Rejection,不会直接抛出到handleException的处理流程中。handleRejection位置错误:你之前将其放在parameter指令内部,而实体大小检查发生在entity解析阶段,此时拒绝已经触发但未被外层处理器捕获。- 拒绝类型匹配错误:之前的代码试图直接匹配
EntityStreamSizeException,但实际需要匹配的是Akka HTTP定义的EntityStreamSizeExceededRejection或包装它的RequestEntityRejection。
内容的提问来源于stack exchange,提问作者Stolaine
相关产品推荐
相关产品推荐

