Search code examples
scalaunmarshallingsprayspray-client

Unmarshalling error onFailure from http POST with spray.client


I am very new to spray.io and scala for that matter, so I apologize in advance if I didn't get the terminology right here in regards to unmarshalling.

I'm trying to get my hands on a HTTP POST response using a pipeline, it's expected to fail because I need an authorization header, but let's ignore that for now.

If I use the chrome extension Postman to POST to the url, I get this response, plain and simple json:

{"status":"error","message":"You must identify yourself to access this resource.","url":"https://scrive.com/login"}

So I started with creating my own case class for this response, and implemented my own JsonProtocol, to unmarshal the response.

case class CreateFromTemplateResult(status: String, message: String, url: String)

object ScriveJsonProtocol extends DefaultJsonProtocol {
  implicit val createFromTemplateResponseFormat = jsonFormat3(CreateFromTemplateResult)
}

So now I'm all set, to run my pipeline, and recieve the response.

val pipeline = sendReceive ~> unmarshal[CreateFromTemplateResult]

val response = pipeline {
   Post("https://api-testbed.scrive.com/api/v1/createfromtemplate/1")
}

response onComplete {
   case Success(CreateFromTemplateResult(status, message, url)) =>
      requestContext.complete("success")

   case Failure(error) =>
      requestContext.complete(error.getMessage)
    }

The request completes and the onFailure case runs, but the error.getMessage is no longer json, as in the previous Postman example.

Status: 403 Forbidden
Body: {"status":"error","message":"You must identify yourself to access this resource.","url":"https://scrive.com/login"}

So my question becomes, how to I unmarshal the error returned and it's Body into my CreateFromTemplateResult case? Or simply into json would do as well. I don't know if my onSuccess case will work either, I want the same behaviour for them both. I want them to be in my defined CreateFromTemplateResult format.

 case class CreateFromTemplateResult(status: String, message: String, url: String)

Thank you very much for your time, and again, I apologize for my bad terminology.


Solution

  • The error in this case is a UnsuccessfulResponseException (you can see from the code to unmarshall). So you can match this case specifically:

    case Failure(e) => e match {
      case ure: UnsuccessfulResponseException =>
        val response = ure.response
        if(response.status = StatusCodes.Forbidden)
          complete(response.as[CreateFromTemplateResult])
        else ...
      case _ => ...
    }
    

    Alternatively you might prefer not to use unmarshal at all (or to write your own unmarshal), and just deal with the HttpResponse that sendReceive returns directly.