【问题标题】:How to handle failed API contracts in frontend applications如何在前端应用程序中处理失败的 API 合约
【发布时间】:2021-12-29 14:32:13
【问题描述】:

我已经开发了几个前端应用程序,当我分析时,我经常需要在以下情况下重构前端代码或前端崩溃:

假设有一个端点 /movies 返回以下响应:

{
  "movieName": "Avengers Endgame",
  "genre": "Action, Thriller",
  "length": 120 // in minutes 
  "rating": 4,
  "id": 1
}

为了在前端应用程序(React + Typescript)中处理此端点的响应,我创建了一个MovieType 并将在组件中呈现数据。

// types.ts

  export type MovieType = {
    movieName: string;
    genre: string;
    length: number;
    rating: number;
    id: number;
  }
import React from 'react';

function MovieComponent() {
  const [movies, setMovies] = React.useState([] as MovieType[]);

  React.useEffect(() => {
    const data:MovieType[] = fetchDataSomeHow() as MovieType[];
    setMovies(data);
  })

  return (
    <ul>
      movies.map(movie => (
        <li key={movie.id}>
          {movie.movieName.toLoweCase()} - {movie.rating}
        </li>
      ))

    </ul>
  )
}

所以在这里我通过查看/movies 端点的响应创建了MovieType,并按照MovieType 中的要求标记了movieName。这里MovieType/movies端点返回的数据影响。

由于我使用的是 Typescript,并且我已将 movieName 定义为 stringrequired,因此我可以使用 toLowerCase 方法而无需任何额外检查。

现在让我们假设电影数据库中有一些错误的输入并且在响应中movieNameundefined/nullmovieName 更改为name。现在前端应用程序会因为运行时错误而崩溃。

每当由于数据库条目错误或后端 API 突然更改而导致前端和后端 API 合同不匹配时,就会发生这种情况。

是否有任何标准或好的方法来处理此类错误?我可以轻松地将每个字段设为可选,然后编写前端代码,但它会变得混乱且不可读。在后端,我们有适配器的概念,可以验证和清理数据并在无效请求的情况下抛出错误,但在前端,我们不能仅仅因为我们在一个对象中的一个字段错误而将用户挂在错误页面上在 1000 个对象中。

【问题讨论】:

  • 也许conditional types 对你有用?
  • 如果你不希望前端失败,你应该有一个后备值。如果缺少电影名称,请说“-”。通常,前端对象与从 API 返回的内容之间会有一个映射阶段,而不是盲目地将 API 响应指向前端对象。在该映射期间,您可以验证并分配空/空回退值。是的,很容易采取任何方式,但最好验证和映射我认为的每个字段。
  • @SanishJoseph - 这正是条件类型将有助于实现的目标。
  • @RandyCasburn 会自动分配回退值吗?说movieName: string | null | "-"。当movieName 为空而不显式分配“-”时,有没有办法获得“-”?
  • 是的,可以做到——点击我上面提供的链接并在该页面上搜索default。单个结果描述了如何执行此操作。

标签: javascript reactjs typescript frontend


【解决方案1】:

您可以使用 && 检查来检查电影是否存在:

movie &amp;&amp; {movie.movieName.toLoweCase()} - {movie.rating}

【讨论】:

  • 这不是可行的解决方案。如果有 100 个键和 20 个 API,那么添加条件语句会使代码非常难看。
【解决方案2】:

我会将类型归类为unknown。大局,你得到了一些东西,但你不知道它是什么。所以是未知数。完成此操作后,Typescript 将强制您检查所有内容,然后您将获得键入结果的所有好处。

你仍然可以定义类型

export type MovieType = {
    movieName: string;
    genre: string;
    length: number;
    rating: number;
    id: number;
  }

但您从 API 获得的结果最初将是 unknown。得到结果后,您将narrow 结果。 unknown 类型将保护您免受此过程中的错误。适当缩小范围后,您可以将其分配给 MovieType 类型或使用该类型。

const result: Record<string, unknown> = {
  movieName: "test",
};

export type MovieType = {
  movieName: string;
  genre: string;
  length: number;
  rating: number;
  id: number;
};

let someMovie: MovieType | undefined;

if (
  typeof result === "object" &&
  typeof result.movieName === "string" &&
  typeof result.genre === "string" &&
  typeof result.length === "number" &&
  typeof result.rating === "number" &&
  typeof result.id === "number"
) {
  someMovie = {
    movieName: result.movieName,
    genre: result.genre,
    length: result.length,
    rating: result.rating,
    id: result.id,
  };
}

不幸的是,我不能直接使用类型unknown 并将其一直缩小到Record&lt;string, unknown&gt;。我问过my own question,但我认为目前可能是不可能的。

【讨论】:

    猜你喜欢
    • 2020-05-28
    • 2021-06-15
    • 2016-01-01
    • 1970-01-01
    • 2019-09-17
    • 1970-01-01
    • 2011-07-15
    • 2018-03-27
    • 1970-01-01
    相关资源
    最近更新 更多