skip to Main Content

I am logging in a user with intentionally invalid credentials and trying to send back the error response from the server ("Unable to login")

How to access this response in my request so I can display the message to the user?

Right now I can print the response object (see below) in Xcode but I don’t see the error message I am trying to access from the server ( "unable to login" ) in this object??

What am I doing wrong?

Javascript Express router code

router.post(`/api/user/login`, async (req, res,) => {
    console.log(req.body)
    try {
        const user = await User.findByCredentials(req.body.email, req.body.password)
        const token = await user.generateAuthToken()
        res.send({user, token})
    } catch (err) {
        const message = err.message     // err.message -> "unable to login"
        res.status(400).send({message}) // HOW DO I ACCESS THIS MESSAGE IN MY REQUEST?
    }
})

Swift API Request

static func loginUser (user: UserLogIn, completion: @escaping(Result <LoginResponse, 
NetworkError>) -> Void) {
        let user = user
        
        guard let url = URL(string: Ref.API_ROOT + Ref.LOGIN) else { return }
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"
        urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
        
        do {
            let encoder = JSONEncoder()
            urlRequest.httpBody = try encoder.encode(user)
      
            let dataTask = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
                
                guard let jsonData = data, error == nil else {
                    if let error = error as NSError?, error.domain == NSURLErrorDomain {

                        completion(.failure(.networkError("(error.localizedDescription)")))
                    }
                    return
                }
                
                guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                    print(response!)
                    completion(.failure(.loginError("Domain Error")))
                    return
                }
               
                do {
                    let response = try JSONDecoder().decode(LoginResponse.self, from: jsonData)
                    completion(.success(response))
                } catch {
                    completion(.failure(.urlBuildError("Url build error"))) 
                }               
            }
            dataTask.resume()
        } catch {
            completion(.failure(.unableToComplete("Unable to complete")))
        }
    }

Response Object :
Why is my error response "unable to login" not in here?

<NSHTTPURLResponse: 0x623453d12134> { URL: http://localhost:3000/api/user/login } { Status Code:
 400, Headers {
    Connection =     (
        "keep-alive"
    );
    "Content-Length" =     (
        30
    );
    "Content-Type" =     (
        "application/json; charset=utf-8"
    );
    Date =     (
        "Wed, 14 Apr 2021 15:38:42 GMT"
    );
    Etag =     (
        "W/"1e-J0jWW/rGxsghjubfgt2iegw3A""
    );
    "Keep-Alive" =     (
        "timeout=5"
    );
    "X-Powered-By" =     (
        Express
    );
} }

2

Answers


  1. Response Object : Why is my error response "unable to login" not in here?

    Because you have added the response in a guard let and your function is returning from there itself, and as it is not able to go ahead it never reaches to the decoding part.

     guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
         print(response!)
         completion(.failure(.loginError("Domain Error"))) 
         return //Returning from here
    }
    

    Here, when the statusCode is not 200 then it goes in the else part, inside which you are printing the response and then, in the next line, you have you completionHandler and then return, so it returns from there only.

    If you want to see your message, you have decode the data in the else part like this:

     guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                    
       do {
           let response = try JSONDecoder().decode(LoginResponse.self, from: data)
           completion(.success(response)) //Here you will get your errorMessage, I have not seen your LoginResponse model, I hope you have added your keys there.
           } catch let error {
                completion(.failure(.urlBuildError(error.localizedDescription))) 
              return 
          }
    }
    

    Here response is URLResponse and not the response you are expecting, please go through the dataTaskMethod and know what it is:

    https://developer.apple.com/documentation/foundation/urlsession/1410330-datatask

    Login or Signup to reply.
  2. For URLSession, HTTPURLResponse class contains only the metadata associated with the HTTP response (documentation) therefore, the logged response seems correct.

    In your case, I believe res.status(400).send({message}) sends the message object in HTTP response’s body. Therefore, to parse that data,

    session.dataTask(with: urlRequest, completion: @escaping(Result <LoginResponse, 
    NetworkError>) -> Void) { (responseBody, response, error) in
        if let error = error {
            // handle transport error
        }
        let response = response as! HTTPURLResponse
        let responseBody = responseBody!
        if !(200...299).contains(response.statusCode) {
            // handle HTTP server-side error
            if let responseString = String(bytes: responseBody, encoding: .utf8) {
                // The response body seems to be a valid UTF-8 string, so print that.
                // This will probably be `{"message": "unable to login"}`
                print(responseString)
            } else {
                // Otherwise print a hex dump of the body.
                print(responseBody as NSData)
            }
            completion(.failure(.loginError("Domain Error")))
            return
        }
        // Handle decoding response...
    }.resume()
    

    You probably would have to do some decoding since you’re sending an object for the error message on node.js. Refer to the Apple docs on Debugging HTTP Server-Side Errors for more information.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search