Recuperar: ¿rechazar la promesa y detectar el error si el estado no es correcto?
Esto es lo que tengo en marcha:
import 'whatwg-fetch';
function fetchVehicle(id) {
return dispatch => {
return dispatch({
type: 'FETCH_VEHICLE',
payload: fetch(`http://swapi.co/api/vehicles/${id}/`)
.then(status)
.then(res => res.json())
.catch(error => {
throw(error);
})
});
};
}
function status(res) {
if (!res.ok) {
return Promise.reject()
}
return res;
}
EDITAR: La promesa no se rechaza, eso es lo que estoy tratando de descubrir.
Estoy usando este polyfill de recuperación en Redux con redux-promise-middleware .
Las promesas de recuperación solo se rechazan con un TypeError cuando ocurre un error de red. Dado que las respuestas 4xx y 5xx no son errores de red, no hay nada que detectar. Deberá generar un error usted mismo para utilizar Promise#catch
.
Una respuesta de recuperación proporciona convenientemente un ok
, que le indica si la solicitud se realizó correctamente. Algo como esto debería funcionar:
fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw new Error('Something went wrong');
})
.then((responseJson) => {
// Do something with the response
})
.catch((error) => {
console.log(error)
});
El siguiente login with username and password
ejemplo muestra cómo:
- Controlar
response.ok
reject
si no está bien, en lugar de arrojar un error- Procese aún más cualquier sugerencia de error del servidor, por ejemplo, problemas de validación.
login() {
const url = "https://example.com/api/users/login";
const headers = {
Accept: "application/json",
"Content-Type": "application/json",
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify({
email: this.username,
password: this.password,
}),
})
.then((response) => {
// 1. check response.ok
if (response.ok) {
return response.json();
}
return Promise.reject(response); // 2. reject instead of throw
})
.then((json) => {
// all good, token is ready
this.store.commit("token", json.access_token);
})
.catch((response) => {
console.log(response.status, response.statusText);
// 3. get error messages, if any
response.json().then((json: any) => {
console.log(json);
})
});
},
Gracias por la ayuda a todos, rechazar la promesa .catch()
resolvió mi problema:
export function fetchVehicle(id) {
return dispatch => {
return dispatch({
type: 'FETCH_VEHICLE',
payload: fetch(`http://swapi.co/api/vehicles/${id}/`)
.then(status)
.then(res => res.json())
.catch(error => {
return Promise.reject()
})
});
};
}
function status(res) {
if (!res.ok) {
throw new Error(res.statusText);
}
return res;
}
Para mí, fny respuestas realmente lo tienen todo. Dado que la recuperación no genera un error, debemos generar/manejar el error nosotros mismos. Publicando mi solución con async/await. Creo que es más sencillo y legible.
Solución 1: no arrojar un error, manejar el error nosotros mismos
async _fetch(request) {
const fetchResult = await fetch(request); //Making the req
const result = await fetchResult.json(); // parsing the response
if (fetchResult.ok) {
return result; // return success object
}
const responseError = {
type: 'Error',
message: result.message || 'Something went wrong',
data: result.data || '',
code: result.code || '',
};
const error = new Error();
error.info = responseError;
return (error);
}
Aquí, si recibimos un error, estamos creando un objeto de error, un objeto JS simple y lo devolvemos, la desventaja es que debemos manejarlo afuera. Cómo utilizar:
const userSaved = await apiCall(data); // calling fetch
if (userSaved instanceof Error) {
debug.log('Failed saving user', userSaved); // handle error
return;
}
debug.log('Success saving user', userSaved); // handle success
Solución 2: arrojar un error usando try/catch
async _fetch(request) {
const fetchResult = await fetch(request);
const result = await fetchResult.json();
if (fetchResult.ok) {
return result;
}
const responseError = {
type: 'Error',
message: result.message || 'Something went wrong',
data: result.data || '',
code: result.code || '',
};
let error = new Error();
error = { ...error, ...responseError };
throw (error);
}
Aquí estamos arrojando un error que creamos, ya que Error ctor aprueba solo una cadena, estoy creando el objeto Error js simple y el uso será:
try {
const userSaved = await apiCall(data); // calling fetch
debug.log('Success saving user', userSaved); // handle success
} catch (e) {
debug.log('Failed saving user', userSaved); // handle error
}
Solución 3: utilizar el error del cliente
async _fetch(request) {
const fetchResult = await fetch(request);
const result = await fetchResult.json();
if (fetchResult.ok) {
return result;
}
throw new ClassError(result.message, result.data, result.code);
}
Y:
class ClassError extends Error {
constructor(message = 'Something went wrong', data = '', code = '') {
super();
this.message = message;
this.data = data;
this.code = code;
}
}
Espero que haya ayudado.
Respuesta de TypeScript 2021
Lo que hago es escribir un fetch
contenedor que toma un genérico y, si lo response
es ok
, se autoafirmará .json()
y escribirá el resultado; de lo contrario, el contenedor arroja elresponse
export const fetcher = async <T>(input: RequestInfo, init?: RequestInit) => {
const response = await fetch(input, init);
if (!response.ok) {
throw response;
}
return response.json() as Promise<T>;
};
y luego detectaré errores y comprobaré si son un archivo instanceof
Response
. De esa manera, TypeScript sabe que error
tiene Response
propiedades como status
statusText
body
headers
etc. y puedo aplicar un mensaje personalizado para cada 4xx
5xx
código de estado.
try {
return await fetcher<LoginResponse>("http://localhost:8080/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ email: "[email protected]", password: "passw0rd" }),
});
} catch (error) {
if (error instanceof Response) {
switch (error.status) {
case 401:
throw new Error("Invalid login credentials");
/* ... */
default:
throw new Error(`Unknown server error occured: ${error.statusText}`);
}
}
throw new Error(`Something went wrong: ${error.message || error}`);
}
y si ocurre algo así como un error de red, se puede detectar fuera del instanceof
Response
control con un mensaje más genérico, es decir
throw new Error(`Something went wrong: ${error.message || error}`);