ios - ¿Cómo detectar y manejar códigos de error HTTP en UIWebView?
objective-c iphone (9)
Quiero informar al usuario cuando se recibe el error HTTP 404, etc. ¿Cómo puedo detectar eso? Ya he intentado implementar
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
pero no se llama cuando recibo el error 404.
Aquí está mi versión swift 3 de la respuesta de @AxelGuilmin:
func webViewDidFinishLoad(_ webView: UIWebView) {
guard let request = webView.request else { return }
let cachedUrlResponse = URLCache.shared.cachedResponse(for: request)
let httpUrlResponse = cachedUrlResponse?.response as? HTTPURLResponse
if let statusCode = httpUrlResponse?.statusCode {
if statusCode == 404 {
// Handling 404 response
}
}
}
En webViewDidFinishLoad:
if ([[(NSHTTPURLResponse*)[[NSURLCache sharedURLCache] cachedResponseForRequest:webView.request] valueForHTTPHeaderField:@"Status"] intValue] == 404){
}
Puede considerar esta solución por encima de otras más complejas, aunque es posible que algunas respuestas no se almacenen en caché. Tenga en cuenta que las direcciones URL incorrectas generalmente se almacenan en caché por un sistema que tiene configuraciones predeterminadas.
Estás malinterpretando para qué sirve -didFailLoadWithError. Técnicamente, la solicitud tuvo éxito. Pudo llegar al servidor y encontrar que el archivo que está solicitando no existe (es decir, 404). El método -didFailLoadWithError será llamado si el servidor no existe, por ejemplo. Su servidor existe. El archivo no lo hace. La vista web no va a interpretar errores en el contenido. El propósito de -didFailLoadWithError de UIWebViewDelegate Apple Docs es:
Se envía si una vista web no pudo cargar contenido.
Del artículo de Wikipedia sobre HTTP 404 :
El mensaje de error 404 o No encontrado es un código de respuesta estándar HTTP que indica que el cliente pudo comunicarse con el servidor, pero el servidor no pudo encontrar lo que se solicitó. Los errores 404 no deben confundirse con "servidor no encontrado" o errores similares, en los que no se pudo establecer una conexión con el servidor de destino.
Con toda probabilidad, tendrá que analizar el texto de respuesta para un 404 que podría obtener con una combinación NSURLConnection / NSURLRequest en lugar de una vista web.
Atentamente,
Mi implementación inspirada en la respuesta de Radu Simionescu:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSCachedURLResponse *urlResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:webView.request];
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*) urlResponse.response;
NSInteger statusCode = httpResponse.statusCode;
if (statusCode > 399) {
NSError *error = [NSError errorWithDomain:@"HTTP Error" code:httpResponse.statusCode userInfo:@{@"response":httpResponse}];
// Forward the error to webView:didFailLoadWithError: or other
}
else {
// No HTTP error
}
}
Administra los errores del cliente HTTP (4xx) y los errores del servidor HTTP (5xx).
Tenga en cuenta que cachedResponseForRequest
devuelve nil
si la respuesta no está en caché, en ese caso statusCode
se asigna a 0 y la respuesta se considera sin errores.
NSURLConnection es la clase que está buscando, no creo que esto se pueda hacer directamente en un UIWebView.
Puedes usar el método síncrono
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
O los asíncronos. Estos son más difíciles de configurar, ya que tiene que adjuntar todos los bits de datos que obtiene en los 1 NSData, pero el resultado final es el mismo.
Independientemente de si utiliza los métodos síncronos o asíncronos:
Si obtiene un objeto NSError *, hubo un error de COMMS. Como se señaló en las otras respuestas, este NO es un código de estado HTTP sino un problema de comunicación.
Si la conexión se realizó correctamente, obtendrás un NSURLResponse y un NSData. ¡Es importante destacar que NSURLResponse para las solicitudes HTTP es en realidad la subclase NSHTTPURLResponse!
Luego debe verificar la respuesta para ver cuál es el código de error. Intente esto (donde _responseInfo
es su objeto NSURLResponse):
NSInteger httpStatusCode = (NSHTTPURLResponse*)_responseInfo.statusCode;
responseInfo siempre debe ser un NSHTTPURLResponse para solicitudes HTTP ... pero puede ser prudente tener una afirmación allí por si acaso.
Si el statusCode es un éxito (es decir, 200), entonces su objeto NSData debe contener los datos de la respuesta (cualquiera que sea). Si el código de estado indica un error, el NSData puede contener una descripción textual del error del servidor.
NÓTESE BIEN. Realmente no recomiendo tyring para analizar el objeto NSData para el mensaje de error. ¡Para eso es el HTTP ''statusCode''!
Podrías capturar el URLRequest aquí:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
y entregar la solicitud al delegado y devolver no. Luego, en la llamada de respuesta recibida de NSURLConnection
cancele la conexión y, si todo está bien (verifique la respuesta), cargue urlrequest una vez más en la vista web. Asegúrese de devolver SÍ en la llamada anterior cuando vuelva a cargar la urlrequest.
No muy elegante, pero podría funcionar.
Soy nuevo en el desarrollo de iOS y Swift, y necesitaba encontrar una manera de lograr esto también, usando WKWebView (no UI). Tuve la fortuna de encontrar este sitio que me dio la siguiente respuesta, que funciona perfectamente para mis necesidades. Podría ayudar a los transeúntes que están buscando la misma respuesta que yo.
Usando esta función (de WKNavigationDelegate):
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void)
Puede crear respuestas personalizadas basadas en la respuesta HTTP, así:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
// get the statuscode
guard let statusCode = (navigationResponse.response as? HTTPURLResponse)?.statusCode
else {
decisionHandler(.allow)
return
}
// respond or pass-through however you like
switch statusCode {
case 400..<500:
webView.loadHTMLString("<html><body><h1>You shall not pass!</h1></body></html>", baseURL: nil)
case 500..<600:
webView.loadHTMLString("<html><body><h1>Sorry, our fault.</h1></body></html>", baseURL: nil)
default:
print("all might be well")
}
decisionHandler(.allow)
}
UIWebView no proporciona ninguna funcionalidad para obtener códigos de estado HTTP para las solicitudes que carga. Una solución es interceptar el proceso de carga de solicitudes de UIWebView usando los métodos UIWebViewDelegate y usar NSURLConnection para detectar cómo responde el servidor a esa solicitud (como se sugirió anteriormente). A continuación, puede tomar una acción adecuada adecuada para la situación. Este artículo explica la solución en detalle en un proyecto de demostración.
Y no es necesario que continúe cargando la solicitud después de recibir una respuesta. Puede simplemente cancelar la conexión en - (void) connection: (NSURLConnection *) connection didReceiveResponse: (NSURLResponse *) método de respuesta después de conocer el código de estado HTTP. De este modo, evitará que la conexión cargue datos de respuesta innecesarios. Luego, puede volver a cargar la solicitud en UIWebView o mostrar un mensaje de error apropiado al usuario según el código de estado HTTP, etc.
Usé el siguiente código para mi solución
-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
NSLog(@"error :%@",error);
NSString *strError = [NSString stringWithFormat:@"%@",error];
if ([strError rangeOfString:@"Code=-1005"].location == NSNotFound) {
NSLog(@"string does not contain Code=-1005");
} else
NSLog(@"string contains Code=-1005”);
}