I bought the next code on-line and I’m making an attempt to switch it in a means that I can get entry to the decoded response for 401. Server sends the next 401 response as JSON.
{ message: 'Username or password is wrong', success: false }
However after I use the HTTPClient under it merely throws NetworkError.unauthorized with out giving me the message. How can I replace the load perform to get entry of the server response.
struct HTTPClient {
personal let session: URLSession
init() {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = ["Content-Type": "application/json"]
self.session = URLSession(configuration: configuration)
}
func load(_ useful resource: Useful resource) async throws -> T {
var request = URLRequest(url: useful resource.url)
// Set HTTP technique and physique if wanted
change useful resource.technique {
case .get(let queryItems):
var parts = URLComponents(url: useful resource.url, resolvingAgainstBaseURL: false)
parts?.queryItems = queryItems
guard let url = parts?.url else {
throw NetworkError.badRequest
}
request.url = url
case .publish(let information), .put(let information):
request.httpMethod = useful resource.technique.title
request.httpBody = information
case .delete:
request.httpMethod = useful resource.technique.title
}
// Set customized headers
if let headers = useful resource.headers {
for (key, worth) in headers {
request.setValue(worth, forHTTPHeaderField: key)
}
}
let (information, response) = attempt await session.information(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}
// Examine for particular HTTP errors
change httpResponse.statusCode {
case 200...299:
break // Success
case 400:
throw NetworkError.badRequest
case 401:
throw NetworkError.unauthorized
case 403:
throw NetworkError.forbidden
case 404:
throw NetworkError.notFound
default:
throw NetworkError.unknownStatusCode(httpResponse.statusCode)
}
do {
let outcome = attempt JSONDecoder().decode(useful resource.modelType, from: information)
return outcome
} catch {
throw NetworkError.decodingError(error)
}
}
}
UPDATE:
NetworkError Appears to be like like the next:
extension NetworkError: LocalizedError {
var errorDescription: String? {
change self {
case .badRequest:
return NSLocalizedString("Unhealthy Request (400): Unable to carry out the request.", remark: "badRequestError")
case .unauthorized:
return NSLocalizedString("Unauthorized (401): Authentication is required.", remark: "unauthorizedError")
case .forbidden:
return NSLocalizedString("Forbidden (403): You do not have permission to entry this useful resource.", remark: "forbiddenError")
case .notFound:
return NSLocalizedString("Not Discovered (404): The requested useful resource couldn't be discovered.", remark: "notFoundError")
case .serverError(let errorMessage):
return NSLocalizedString(errorMessage, remark: "serverError")
case .decodingError:
return NSLocalizedString("Unable to decode efficiently.", remark: "decodingError")
case .invalidResponse:
return NSLocalizedString("Invalid response.", remark: "invalidResponse")
case .unknownStatusCode(let statusCode):
return NSLocalizedString("Unknown error with standing code: (statusCode).", remark: "unknownStatusCodeError")
}
}
}
UPDATE 2:
I up to date the load perform and now after I get 401 I throw LoginError. Does that look proper? Since HTTPClient is a generic community layer it feels bizarre to throw such particular errors?
// Examine for particular HTTP errors
change httpResponse.statusCode {
case 200...299:
break // Success
case 400:
throw NetworkError.badRequest
case 401:
let response = attempt? JSONDecoder().decode(ErrorResponse.self, from: information)
throw LoginError.loginFailed(response?.message ?? "Login error")
case 403:
throw NetworkError.forbidden
case 404:
throw NetworkError.notFound
default:
throw NetworkError.unknownStatusCode(httpResponse.statusCode)
}
UPDATE 3:
Contained in the HTTPClient load perform, I up to date the code to throw the identical NetworkError as proven under:
case 401:
let response = attempt JSONDecoder().decode(ErrorResponse.self, from: information)
throw NetworkError.unauthorized(response)
The caller (AuthenticationController) handles the decision after which throw the suitable error.
func login(username: String, password: String) async throws -> LoginResponse {
let physique = ["username": username, "password": password]
let bodyData = attempt JSONEncoder().encode(physique)
let useful resource = Useful resource(url: Constants.login, technique: .publish(bodyData), modelType: LoginResponse.self)
do {
let response = attempt await httpClient.load(useful resource)
return response
} catch NetworkError.unauthorized(let errorResponse) {
throw LoginError.loginFailed(errorResponse.message ?? "Unable to login.")
} catch {
throw error
}
}