如何使用 React Apollo 客户端处理 HTTP 状态错误

Posted

技术标签:

【中文标题】如何使用 React Apollo 客户端处理 HTTP 状态错误【英文标题】:How to handle HTTP status errors with React Apollo Client 【发布时间】:2021-12-04 14:18:08 【问题描述】:

我在后端有一个使用 apollo-server-express 制作的简单登录应用程序。如果用户输入了无效的用户名或密码,则会抛出 401 Unauthorized 异常,如下所示:

async login(username: string, password: string) 
     const user = await this.userService.findOneByUsername(username);
     if (!user) 
         throw new UnauthorizedException("The username doesn't exists");
     
     const validPassword = bcrypt.compareSync(password, user.password);
     if (!validPassword) 
         throw new UnauthorizedException('The password is incorrect');
     
     return user;

现在,在我的 React 客户端中,我使用 Apollo Client 来使用服务。我是这样登录的:

const [login,  error, loading ] = useLoginMutation(
  variables: 
    username,
    password,
  ,
);

const onFormSubmit = async (e: React.FormEvent<htmlFormElement>) => 
  e.preventDefault();
  const response = await login();
  if (response) 
    if (response.data) 
      localStorage.setItem("user", response.data.login.jwt);
      addUser(response.data.login.user);
    
  
;

现在,当我输入错误的用户名或密码时,后端会抛出异常,但这会使 React 应用程序停止。这可以通过在 login() 方法之后添加一个 catch 语句来解决,如下所示:

const response = await login().catch(e =>  );

但我想知道是否有一种优雅的方式来捕获客户端中的异常或将其扔到服务器中。在 REST 服务中,像 401 这样的错误不会使应用程序停止,请求只会失败但应用程序仍在运行,但我是 GraphQL 的新手,我想知道 GraphQL 服务中是否不是这样。 有没有更好的方法来处理或抛出 GraphQL 和 Apollo 的 Http 错误?

【问题讨论】:

当您说“优雅地在客户端捕获异常或将其扔到服务器中”时,我不明白您在问什么。 @rayhatfield 我想知道更好的方法来处理客户端中的 HTTP 异常,在这种情况下,是带有 Apollo 客户端的 React 应用程序,或者将它们扔到服务器中,在这种情况下,使用 apollo-server-express 的 NestJs 应用 您可以将 await login 的东西包装在 try/catch 中,或者将整个东西移动到处理它的自定义 useLogin 钩子中,这样单个组件就不必处理它,但我我仍然不确定我明白你在问什么。 【参考方案1】:

问题

async/await 语句必须在 try/catch 块中。有关MDN 文档的更多信息。

then/catch 是另一种处理异步承诺的方式,但结果与async/await 相同,所以不要将它们混在一起。

解决方案

使用当前实现 (async/await),您需要将异步获取函数包装在 try/catch 块中以控制所有副作用(成功和失败场景)。

const onFormSubmit = async (e: React.FormEvent<HTMLFormElement>) => 
  e.preventDefault();
  try 
    const response = await login();
    if (response?.data) 
        localStorage.setItem("user", response.data.login.jwt);
        addUser(response.data.login.user);
    
   catch (error) 
    // do a proper action in error cases
    const myErrorStatus = error.response?.status
    if(myErrorStatus === 401) 
      // do this
     else if (myErrorStatus === 404) 
      // do this
     
  
;

可选:

注意:不要使用嵌套的if 块来检查response,您可以使用? 速记。例如:

if(a)
  if(a.b)
    if(a.b.c)
      // ...
    
  

可以简化为:

if(a?.b?.c)
 // ...

【讨论】:

以上是关于如何使用 React Apollo 客户端处理 HTTP 状态错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React 中为上传和订阅设置 Apollo 客户端?

使用 Apollo 客户端处理前端的渐进式披露

如何使用 GQL 从 React 前端的 Apollo 客户端获取错误

Apollo+React:如何使用代理使客户端和 graphql 在同一个域上?

如何防止我的 Apollo 客户端错误处理程序(onError,错误链接)因同一错误被调用两次?

何时以及如何使用 Apollo 客户端和 React 路由器进行重定向