惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
博客园 - 三生石上(FineUI控件)
MyScale Blog
MyScale Blog
Apple Machine Learning Research
Apple Machine Learning Research
L
LINUX DO - 最新话题
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
宝玉的分享
宝玉的分享
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
GbyAI
GbyAI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Visual Studio Blog
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
月光博客
月光博客
博客园 - 叶小钗
D
Docker
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Tailwind CSS Blog
D
DataBreaches.Net
酷 壳 – CoolShell
酷 壳 – CoolShell
B
Blog RSS Feed
量子位
美团技术团队
Vercel News
Vercel News
Y
Y Combinator Blog
IT之家
IT之家
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
SegmentFault 最新的问题
腾讯CDC
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
G
Google Developers Blog
Microsoft Azure Blog
Microsoft Azure Blog
The Register - Security
The Register - Security
博客园 - 司徒正美
N
Netflix TechBlog - Medium
S
Schneier on Security
博客园 - 聂微东
U
Unit 42
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
雷峰网
雷峰网
Latest news
Latest news

动物的光合作用

使用 Peer Relay 提高 Tailscale 的速度 内网穿透下 NAS 使用 DNS 轮循作为负载均衡 <众安车保>上当指南 - 动物的光合作用 JS 中的 Shebang/Hashbang - 动物的光合作用 我的自建 NAS - 动物的光合作用 React 组件中复用代码的方式 - 动物的光合作用 负载均衡下的前端资源更新策略 - 动物的光合作用 CSS 基于视区的长度单位 - 动物的光合作用 Excalidraw 支持自定义字体 - 动物的光合作用 我用 Docker 部署的一些服务 - 动物的光合作用 CSS 书写模式和逻辑属性 - 动物的光合作用 大量活动页面持续性维护的解决方案 - 动物的光合作用 Mac mini 搭建外网可访问的简易 NAS - 动物的光合作用 React Fragment 添加事件监听? - 动物的光合作用 如何在 React 解决竞态条件 - 动物的光合作用 使用脚本备份 Github 仓库 - 动物的光合作用 高性能 React 应用的几个小技巧 - 动物的光合作用 复习 DOM 事件 - 动物的光合作用 我的博客架构变迁之路 - 动物的光合作用 浏览器和 JavaScript 的一些新特性 - 动物的光合作用 useCallback 的误区 - 动物的光合作用 在 React 中使用事件分离状态 - 动物的光合作用 基于 Node.js 的 WebFont 解决方案
如何处理 RESTFUL 数据异常导致的前端错误 - 动物的光合作用
mebtte · 2019-05-30 · via 动物的光合作用

最近一段时间监控平台收集了很多 JS 错误,大部分都是TypeError

Uncaught TypeError: Cannot read property 'xxx' of null

经过排查后发现基本都是因为接口数据错误的导致,比如

// 正常数据
{
  "data": {
    "name": "xxx",
  },
}

// 错误数据
{
  "data": null,
}

data 应该是一个对象,如果返回 null 的话访问data.name就会产生TypeError。 至于接口为什么会返回错误的数据,原因有很多种

  • 接口本身 BUG
  • 手动修改数据库
  • 其他接口 BUG 导致错误地修改了数据

修复这些错误其实很简单,比如访问data之前先判断data是否为null

data ? data.name : '';
// 在其他的一些语言中有`?.`操作符可以很方便地进行上面的操作

但同时也会带来一些问题。

比如name是要显示在页面上,我的名字是xxx,我喜欢唱跳RAP和篮球,因为缺失了name,所以变成了我的名字是,我喜欢唱跳RAP和篮球。 这会对用户造成一些困扰。 在一些涉及到交易的场景可能会造成更大的麻烦。

另一个问题是如果接口数据复杂一点的话,为了避免报错你的页面可能会有很多判断的语句

{
  data: {
    user: { name: 'xxx' },
    theme: { color: 'red' },
  },
}

// 访问`user.name`
data ? (data.user ? data.user.name : '') : '';
// 或者
(data && data.user && data.user.name) || '';

// 访问`theme.color`
data ? (data.theme ? data.theme.color : '') : '';
// 或者
(data && data.theme && data.theme.color) || '';

对于这类问题,我的观点是数据异常的页面不应该展示给用户。所以,应该对接口的数据进行校验。

npm上有很多优秀的数据校验包,比如 ajvjoi 这些,以joi为例。

统一接口格式

{
  error_code: "错误码,正常为0",
  error_message: "错误信息,正常为''",
  data: "数据",
}

封装请求

// request.js
import axios from 'axios'; // 或 window.fetch

const request = async (options) => {
  const response = await axios(options);
  const {
    error_code: errorCode,
    error_message: errorMessage,
    data,
  } = response.data;
  if (errorCode !== 0) {
    throw new Error(`${errorCode}:${errorMessage}`);
  }

  // shcema 表示 joi 对象
  // 验证数据,部分接口可能不需要验证,所以 schema 是可选参数
  if (options.schema) {
    // validate 是 joi 对象自带的方法用于验证数据
    const { error } = schema.validate(data);

    // 数据验证失败
    if (error) {
      // 上报数据格式错误,对于一些简单的数据错误可以及时修改数据库
      reportDataError();
      throw error;
    }
  }

  return data;
};

export default request;

react为例,调用请求

import React, { useState, useEffect } from 'react';
import Joi from '@hapi/joi';

import request from './path/request';

// schema
const schema = Joi.object().keys({
  user: Joi.object().keys({
    name: Joi.string().required(),
  }),
  theme: Joi.object().keys({
    color: Joi.string().required(),
  }),
});

const Component = () => {
  const [status, setStatus] = useState('loading');
  const [data, setData] = useState(null);

  useEffect(() => {
    request({
      method: 'get',
      url: '/user',
      schema,
    })
      .then((user) => {
        setStatus('success');
        setUser(user);
      })
      .catch(() => setStatus('error'));
  }, []);

  if (status === 'success') {
    // 保证了可以正确地获取变量,渲染过程中不会报错
    return <div style={{ color: data.theme.color }}>{data.user.name}</duv>;
  }
  if (status === 'loading') {
    return <div>loading</div>;
  }
  return <div>error</div>;
};

当调用请求的时候,数据格式或数据出错可以显式地抛出错误让组件捕获,可以保证用户看到的页面一定是正确的,否则只会看到错误页面。 可以对请求进一步封装,如果多个组件调用就不需要重复定义schema

// getUserInfo.js
import Joi from 'joi';

import request from './path/request';

const schema = Joi.object().keys({
  user: Joi.object().keys({
    name: Joi.string().required(),
  }),
  theme: Joi.object().keys({
    color: Joi.string().required(),
  }),
});

export default () =>
  request({
    method: 'get',
    url: '/user',
    schema,
  });

当然,上面只是前端的一种预防方案,最重要应该还是在接口层面保证数据的完整性和正确性。