GraphQL 的错误处理:如何避免大致流和客户端缓存不一致的问题

阅读时长 8 分钟读完

前言

GraphQL 是一种用于 API 接口的查询语言,它让前端可以只请求自己需要的数据,而避免了 RESTful API 那样返回大量冗余的数据。

然而,真正利用好 GraphQL 也需要一定技巧。本文将阐述在使用 GraphQL 时,错误处理的相关问题,包括大致流和客户端缓存不一致等问题,并提供相应解决方案和示例代码。希望本文能对理解 GraphQL 以及前端开发有所启发。

背景

在使用 GraphQL 时,我们会通过发送一个长长的查询语句获取所需数据。正常情况下,后端会返回客户端所需的数据。但如果没有处理好错误情况,则可能出现以下问题:

大致流

大致流(inexact flow)指的是,客户端在获取数据时,数据状态(例如数据是否加载完毕、是否有错误等)与实际有出入。例如,在完成某个任务之前,客户端已经收到了一部分的数据并进行了数据展示,但实际上这些数据只是一个中间状态,会在后续的操作中进行处理。

如上图所示,客户端直接从服务端获取到了查询结果中的一部分,这时候客户端并没有意识到数据未加载完毕,渲染了未完成的内容。

客户端缓存不一致

GraphQL常常会引入客户端缓存,以优化应用性能。但如果不加以限制,就会导致客户端与服务端的数据不一致,令用户看到不正确或不完整的数据。

如上图所示,客户端在接收到服务端数据后,将数据缓存在本地客户端,然后用户操作触发新的查询(例如筛选),客户端经过本地缓存直接返回数据,但其实服务端的数据已经更新了,导致返回的数据不一致。

解决方案

为解决大致流和客户端缓存不一致问题,我们需要在GraphQL API 的错误处理上下功夫。具体实现有以下方案:

1. 通过错误码区分不同错误

GraphQL 应用中,通常会定义不同的错误码,针对不同的错误返回特殊的错误信息。例如,HTTP 中的 404 就是一种常用的错误码。

在 GraphQL 中,我们可以通过定义一套自己的错误码进行处理:

-- -------------------- ---- -------
---- ----- -
  -------- ----- ----
-

---- ---- -
  --- ---
  ------ -------
  ----- -------
  -------- ------
  ------------ --------
-

---- --------- -
  --------------
-

---- ---------- -
  ----- ----------
-

----- ----------- - ---------- - ----

这里我们自定义了一个错误码 ErrorCode,来区分不同的错误。错误信息会以 QueryError 类型返回,客户端可以根据错误码来判断错误类型并进行不同处理。

2. 通过 errors 字段返回多个错误

在 GraphQL API 中,如果出现多个错误,则使用 errors 字段返回。 errors 是一个包含多个错误对象的列表,每个错误对象都有一个描述信息和错误码。

-- -------------------- ---- -------
---- ----- -
  -------- ----- ----
-

---- ---- -
  --- ---
  ------ -------
  ----- -------
  -------- ------
  ------------ --------
-

---- ---------- -
  ----- ----------
-

----- ----------- - ---------- - ----

---- ------------- -
  ------- -----------
  ------- --------------
-

这里我们定义了一个新类型 QueryResponse,用于包装 resulterrors 信息,并以此来统一返回数据和错误信息。

3. 通过 response.extensions 传递额外信息

为避免客户端缓存造成数据不一致,我们可以借鉴 HTTP 缓存控制的做法,通过 Cache-Control 头部设置缓存控制策略,或者通过响应头部返回 ETag 或者 Last-Modified 等信息,来避免客户端缓存不一致情况的出现。

在 GraphQL 中,可以使用 extensions 返回一些额外的信息。其中,cacheControl 字段可以用于设置客户端缓存策略。

-- -------------------- ---- -------
---- ----- -
  -------- ----- ----
-

---- ---- -
  --- ---
  ------ -------
  ----- -------
  -------- ------
  ------------ --------
-

---- ---------- -
  ----- ----------
-

----- ----------- - ---------- - ----

---- ------------- -
  ------- -----------
  ------- --------------
-

---- ------------ -
  --------- --------------
  ----------- ----------------
-

---- --------------- -
  ------------- -------------
-

---- ------------ -
  ------- ----
-

在上面的示例中,我们定义了一个新的类型 QueryPayload 用于包装响应数据以及缓存相关信息。 QueryExtensions 中新增了一个 cacheControl 字段,用于表示缓存控制信息。CacheControl 中的 maxAge 字段则决定了客户端如何缓存数据。

示例代码

-- -------------------- ---- -------
---- ----- -
  -------- ----- ----
-

---- ---- -
  --- ---
  ------ -------
  ----- -------
  -------- ------
  ------------ --------
-

---- --------- -
  --------------
-

---- ---------- -
  ----- ----------
-

----- ----------- - ---------- - ----

---- ------------- -
  ------- -----------
  ------- --------------
-

---- ------------ -
  ------- ----
-

---- --------------- -
  ------------- -------------
-

---- ------------ -
  --------- --------------
  ----------- ----------------
-

---- ----- -
  -------- ----- -------------
-

------ -
  ------ -----
-

在上面的示例代码中,我们通过定义 QueryPayload,并在 Query 中返回该类型,以便包装缓存信息和错误信息,并避免无意间缓存客户端数据导致不一致的问题。

-- -------------------- ---- -------
------ - ------- - ---- -----------------

--------- ---- -
  --- ------
  ------ ------
  ------ ------
  --------- ------
  ------------- --------
-

--------- ---------- -
  ----- ------
-

--------- ----------- -
  -------- ----
  -------- ------------
-

--------- ------------- -
  ----- -----------
  ------------ -
    ------------- -
      ------- ------
    -
  -
-

----- --------- - ----- ---- -------- -------------------- -- -
  ----- ----- - -
    ----- ----- ---- -
      -------- ---- -
        -------- -
          ------ -
            --
            -----
            ----
            -------
            -----------
          -
        -
        ------ -
          ----
        -
        ---------- -
          ------------ -
            ------
          -
        -
      -
    -
  -
  ----- --------- - -
    ---
  -

  ----- -------- - ----- ---------------------------------- ------ ----------
  ------ -------------
-

----- ------ - ----- ----------------
----- - ------- ----- ------ - - ------

-- ------ -
  -- -- ---------
- ---- -- --------------- -
  -- ------ ------
-

在上面的示例代码中,我们使用 graphql-request 来发送 GraphQL 查询请求,并解析响应中的数据以及错误信息。同时,也获取了返回的缓存信息。

结论

本文详细阐述了 GraphQL 错误处理的相关问题,并提供了相应的解决方案和示例代码。希望这篇文章能够对前端开发人员在使用 GraphQL 时,解决大致流和客户端缓存不一致问题,有所启发。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6749b6d6a1ce0063546dd52d

纠错
反馈