scalasprayjson4sspray-dsl

Different routes based on request content type Spray Routing 1.2.1


I would like to support a couple of different content types submitted to the same URL:

e.g:

application/x-www-form-urlencoded, multipart/form-data, application/json

I would like to do something like:

post {
  contentType(`application/x-www-form-urlencoded`) | 
  contentType(`multipart/form-data`) {
     // user POSTed a form
     entity(as[MyCaseClass]) { data =>
        complete { data.result }
     }
  } ~ contentType(`application/json`) {
     // user POSTed a JSON object
     entity(as[MyCaseClass]) { data =>
        complete { data.result }
     }
  }
}

I think there may be some way to do this with custom marshaling and unmarshaling, but I only need it in one or two spots in my service and this seems pretty simple. Can someone help?


Solution

  • There is a really elegant way to achieve this thanks to deep cleverness in the Spray marshalling system. The code (gist) illustrates this:

    case class User(name: String, no: Int)
    
    // Json marshaller
    object UnMarshalling extends DefaultJsonProtocol {
      val jsonUser = jsonFormat2(User)
      val textUser = Unmarshaller[User](`text/plain`) {
          case HttpEntity.NonEmpty(contentType, data) =>
            val res = data.asString.drop(5).dropRight(1).split(',')
            User(res(0),res(1).toInt)
      }
      implicit val userMarshal = Unmarshaller.oneOf(jsonUser, textUser)
    }
    
    class UnMarshalTest extends FunSpec with ScalatestRouteTest with Matchers {
      import UnMarshalling._
    
      // Marshals response according to the Accept header media type
      val putOrder = path("user") {
        put {
          // Namespace clash with ScalaTestRoutes.entity
          MarshallingDirectives.entity(as[User]) {
            user =>
              complete(s"no=${user.no}")
          }
        }
      }
    
      describe("Our route should") {
    
        val json = """ {"name" : "bender", "no" : 1234} """
    
        it("submit a json") {
          Put("/user", HttpEntity(`application/json`,json)) ~> putOrder ~> check {
            responseAs[String] should equal("no=1234")
          }
        }
        it("Submit text") {
          Put("/user", HttpEntity(`text/plain`,"""User(Zoidberg,322)""")) ~> putOrder ~> check {
            responseAs[String] should equal("no=322")
          }
        }
      }
    }