Skip to content

Instantly share code, notes, and snippets.

@etaty
Created November 5, 2015 17:30
Show Gist options
  • Save etaty/96738702fd1d7b74ccf4 to your computer and use it in GitHub Desktop.
Save etaty/96738702fd1d7b74ccf4 to your computer and use it in GitHub Desktop.
json circe play framework body parser
package utils
import java.nio.ByteBuffer
import cats.data.Xor
import io.circe.Json
import play.api.http.LazyHttpErrorHandler
import play.api.http.Status._
import play.api.libs.iteratee.{Iteratee, Traversable}
import play.api.mvc.BodyParsers.parse._
import play.api.mvc._
import scala.concurrent.Future
object JsonCirceBodyParser {
val jsonCirceBodyParser = {
BodyParser[Json]("json (circe)"){ requestHeader =>
import play.api.libs.iteratee.Execution.Implicits.trampoline
Iteratee.consume[Array[Byte]]().mapM[Either[Result, Json]] { bytes =>
val maybeJson = io.circe.jawn.parseByteBuffer(ByteBuffer.wrap(bytes))
maybeJson match {
case Xor.Left(error) =>
createBadResult("json parsing error: " + error.getMessage())(requestHeader).map(Left.apply)
case Xor.Right(json) => Future.successful(Right(json))
}
}
}
}
def tolerantJson[A](maxLength: Int, parser: BodyParser[A]): BodyParser[A] = {
BodyParser[A]("maxLength=" + maxLength + ", wrapping=" + parser.toString) { requestHeader =>
import play.api.libs.iteratee.Execution.Implicits.trampoline
Traversable.takeUpTo[Array[Byte]](maxLength).transform(parser(requestHeader))
.flatMap(Iteratee.eofOrElse(MaxSizeExceeded(maxLength)))
.mapM {
case Right(Right(result)) => Future.successful(Right(result))
case Right(Left(badRequest)) => Future.successful(Left(badRequest))
case Left(maxSizeExceeded) => createBadResult("Request Entity Too Large", REQUEST_ENTITY_TOO_LARGE)(requestHeader).map(Left.apply)
}
}
}
def json = when(
_.contentType.exists(m => m.equalsIgnoreCase("text/json") || m.equalsIgnoreCase("application/json")),
tolerantJson(DefaultMaxTextLength, jsonCirceBodyParser),
createBadResult("Expecting text/json or application/json body", UNSUPPORTED_MEDIA_TYPE)
)
def createBadResult(msg: String, statusCode: Int = BAD_REQUEST): RequestHeader => Future[Result] = { request =>
LazyHttpErrorHandler.onClientError(request, statusCode, msg)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment