Recuperar: ¿rechazar la promesa y detectar el error si el estado no es correcto?

Resuelto Vlady Veselinov asked hace 8 años • 14 respuestas

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 .

Vlady Veselinov avatar Jul 07 '16 07:07 Vlady Veselinov
Aceptado

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)
});
fny avatar Jul 07 '2016 01:07 fny

El siguiente login with username and passwordejemplo muestra cómo:

  1. Controlarresponse.ok
  2. rejectsi no está bien, en lugar de arrojar un error
  3. 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);
      })
    });
},

ohho avatar May 23 '2021 14:05 ohho

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;
}
Vlady Veselinov avatar Jul 07 '2016 01:07 Vlady Veselinov

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.

Tomer Omri avatar Jul 07 '2020 11:07 Tomer Omri

Respuesta de TypeScript 2021

Lo que hago es escribir un fetchcontenedor que toma un genérico y, si lo responsees 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 errortiene Responsepropiedades como status statusText body headersetc. y puedo aplicar un mensaje personalizado para cada 4xx 5xxcó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 Responsecontrol con un mensaje más genérico, es decir

throw new Error(`Something went wrong: ${error.message || error}`);
Jarod avatar Mar 19 '2021 18:03 Jarod