scalaakka-http

How to catch EntityStreamSizeException thrown by withSizeLimit Directive


I have created a post request in akka http to upload a file

Here is my code

final val MEDIA_FILE_UPLOAD_MAX_SIZE = 30 * 1024 * 1024

def uploadMediaObjectForASite(): Route = 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)
                  // extract file parts and start the upload process
              }
            }
          }
      }
    }
}

If I use a file greater than 30MB I get a 400 BadRequest in postman with following message

EntityStreamSizeException: incoming entity size (47350868) exceeded size limit (31457280 bytes)! This may have been a parser limit (set via akka.http.[server|client].parsing.max-content-length), a decoder limit (set via akka.http.routing.decode-max-size), or a custom limit set with withSizeLimit.

I have tried following approach a) using handleException directive

 val customExceptionHandler: ExceptionHandler = ExceptionHandler {
    case _: EntityStreamSizeException =>
      complete(StatusCodes.PayloadTooLarge, "File size exceeded the limit of 30MB")
  }

def uploadMediaObjectForASite(): Route = path("Some path") {
  userId =>
    post {
        handleException(customExceptionHandler) {
            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)
                      // extract file parts and start the upload process
                  }
                }
              }
          }
        }
    }
}

b) using handleRejection directive

val customRejectionHandler: RejectionHandler = RejectionHandler.newBuilder()
    .handle {
      case EntityStreamSizeException(limit, actualSize) =>
        complete(StatusCodes.PayloadTooLarge, s"File size exceeded the limit of ${limit} bytes")
    }
    .result()

def uploadMediaObjectForASite(): Route = path("Some path") {
  userId =>
    post {
        handleRejection(customRejectionHandler) {
            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)
                      // extract file parts and start the upload process
                  }
                }
              }
          }
        }
    }
}

But none of them works

How can I catch this exception and provide custom message.


Solution

  • You may use the directives handleExceptions()/handleRejections() like this:

      val handleErrors = handleExceptions(customExceptionHandler) &  handleRejections(customRejectionHandler)
    
      val routes: Route = {
        handleErrors {
          concat(uploadMediaObjectForASite, myOtherRoute)
        }
      }
    

    Also see this working example. Not sure why your approach is not working though, although I just see the handleExceptionsdirective in 10.5.2 (and not handleException). Also I see that the response PayloadTooLarge is deprecated in favour of ContentTooLarge.