Alamofire: Get response data even when validation fails to pass

What I am trying to achieve is to read the response data even when the validation set, fails. The reason for doing that is because the API I am trying to access implements application/problem+json.

So:

Alamofire.request(.POST, "http://httpbin.org/post")
  .validate()
  .responseJSON { (_, _, JSON, error) in
    println(JSON)        // null
    println(error)       // Error Domain=com.alamofire.error Code=-1 "The operation couldn’t be completed. (com.alamofire.error error -1.)"
  }

fails as it should when status code is for example 400, although I can’t read the data inside response body by accessing the JSON.

I am currently trying to write an extension of Request for that, but without luck so far.

Thanks @mattt for this great library.

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Comments: 18 (6 by maintainers)

Most upvoted comments

Hey @mattt First of all, thanks a lot for making this great library.

We are having issues with this right now as well. The HTTP/1.1 RFC specifies that the response for 4xx requests should return an entity contain an explanation of an error. Is there a way that we could have Alamofire support this case?

EDIT: RFC Here

Let’s say that we are doing a POST which returns a 429 with these data:

XHR Response: {
  "status": 429, 
  "type": "https://sample.com/error/server-error", 
  "detail": "Some description text", 
  "title": "Rate limit”
}

NOTE: I am using validate(statusCode: 200..<400).

and now the ResponseSerializer with some pseudo code.

return ResponseSerializer { _, response, data, error in

            // a custom object that app uses
            let res = ResponseHandler()

            guard error == nil else {

                // set error in ResponseHandler
                // this will allows us to use .Success for now
                res.error = error

                // try to parse response data as JSON
                // if the response is an application/problem+json
                // it will create a JSON successfully
                // after that we will try to read its status
                if response?.statusCode != 204,
                    let validData = data,
                    let headers = response?.allHeaderFields
                        where headers["content-type"]?.lowercaseString.rangeOfString("application\\/problem\\+json", options: .RegularExpressionSearch) != nil
                {                    
                    // add problem+json to response
                    res.json = JSON(data: validData)
                }

                // NOTE: we want to parse `application/problem+json` so when available we use .Success() to pass the object back
                // return .Success(res)

                return .Failure(res, error)
            }

            guard let validData = data where validData.length > 0 else {
                let failureReason = "JSON could not be serialized. Input data was nil or zero length."
                let error = AQError.errorWithCode(.AQJWTSerialization, failureReason: failureReason)

                res.error = error

                // NOTE: we want to parse `application/problem+json` so when available we use .Success() to pass the object back
                // return .Success(res)

                return .Failure(res, error)
            }

            if response?.statusCode != 204 {

                // parse it as a SwiftyJSON
                res.json = JSON(data: validData)
            }

            return .Success(res)
        }

As you see above normally I am using .Success instead of .Failure. The benefit that I get the error data parsed as JSON in this case.

I have two serializers which depend on such logic.