Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 929
Sam in Oakland Avatar asked Apr 03 '14 01:04

Sam in Oakland


1 Answers

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")
      }
    }
  }
}
like image 122
David Weber Avatar answered Oct 22 '22 03:10

David Weber