8

In spray I would like to respond with different content-types, depending on the given Accept header. I've seen a couple of suggestions in the question by rompetroll, but I would like to hear if there are any canonical way of doing it (i. e. simple or already implemented).

In essence what I imagine should happen is something like:

path("somepath") {
  get {
    // Find whatever we would like to return (lazily)
    ...
    // Marshall resource and complete depending on the `Accept` header
    ...
  }
}

Thanks in advance.

Community
  • 1
  • 1
Jens Egholm
  • 2,730
  • 3
  • 22
  • 35

2 Answers2

15

See the tests in this commit.

I copied it here for reference:

case class Data(name: String, age: Int)
object Data {
  import spray.json.DefaultJsonProtocol._
  import spray.httpx.SprayJsonSupport._

  // don't make those `implicit` or you will "ambiguous implicit" errors when compiling
  val jsonMarshaller: Marshaller[Data] = jsonFormat2(Data.apply)
  val xmlMarshaller: Marshaller[Data] =
    Marshaller.delegate[Data, xml.NodeSeq](MediaTypes.`text/xml`) { (data: Data) ⇒
      <data><name>{ data.name }</name><age>{ data.age }</age></data>
    }

  implicit val dataMarshaller: ToResponseMarshaller[Data] =
    ToResponseMarshaller.oneOf(MediaTypes.`application/json`, MediaTypes.`text/xml`)  (jsonMarshaller, xmlMarshaller)
}

You then using complete should suffice in your route, content-type negotiation is automatically taken care of:

get {
  complete(Data("Ida", 83))
}
jrudolph
  • 8,307
  • 4
  • 32
  • 50
  • Silly question, but in the call `jsonFormat2(Data.apply)` above, where is the `.apply` defined? – ivcheto Jun 22 '15 at 02:30
  • It's automatically generated by the Scala compiler for a `case class` to support the `Data(...)` syntax instead of needing to write `new Data(...)`. – jrudolph Jun 22 '15 at 05:47
8

Spray is actually looking into the Accept header value and validates against it. So if route is returning application/json or text/plain and client accepts image/jpeg than spray will return 406 Not Acceptable. If client will request application/json ortext/plain from this route than he will receive repsonse with matching Content-Type.

The main trick here is to use correct marshallers for return objects. You can read more about marshalling here.

Also you can override MediaType with respondWithMediaType directive, but I think it is better to use correct marshallers.

vitalii
  • 3,335
  • 14
  • 18
  • That makes a lotta sense. I totally missed this from the documentation. Thanks :) – Jens Egholm Jan 14 '14 at 20:42
  • 1
    Agreed, `respondWithMediaType` is usually the wrong way to go. Marshallers already contain all the logic to do automatic content-type negotiation. See my answer how to combine marshallers for different content-types into one that accepts all of them and chooses the right marshaller for all of them. – jrudolph Jan 15 '14 at 09:22