我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本文的内容主要分为两个方面,一个是使用express做个接口服务,第二个是如何请求这个接口。以前也经常写这方面的内容,这里就是新瓶装老酒。这里单独成篇一篇内容,以后就直接引用这篇文章,少写内容了。

苏南大叔:NodeJs及浏览器环境,真实GET请求远程接口方案总结 - 请求远程接口
NodeJs及浏览器环境,真实GET请求远程接口方案总结(图1-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0express@4.21.1。由于篇幅限制,本文仅表述GET的情况,POST的情况另开文章。

接口请求原则

服务器端api来说,

  • 返回的数据必然是个JSON字符串,返璞归真,只有字符串类型适合这种数据传播场景。服务器端极有可能是个JSON object=>JSON.stringify()=>JSON string的过程。
  • 服务器端的返回值,应该可以通过JSON.parse(s)转为json格式,内容上可以是成功正常数据,也可能是人为定义的发生错误数据。
  • 服务器端状态码,可以视场景修改状态码。对于自己自定义错误的情况,最好别修改状态码,徒增工作量。

客户端请求来说,

  • 正常情况来说,拿到的数据,必然是个JSON字符串。可能需要个JSON.parse(s)或者response.json()的过程。
  • 如果服务器端抛出异常,对于本地代码来说,只是reponse.ok一个变量的问题,并不会触发异常catch的。如果需要对这种情况进行处理,请在本地根据response.okresponse.text()再次抛出这个异常。
  • 如果客户端是在纯node程序里面请求的话,并不需要服务器端设置跨域cors。浏览器环境下的话,就看情况设置跨域了。

Error兼容

以下几种情况,是会被本地代码视为被catch()的对象,但是不一定是个Error,也可能是个仿Error{message:''}

  • 服务器端修改状态码非200,本地解析response,根据!.okthrow a Error("")。如果这里需要明确获得服务器消息的话,这里还需要await response.text()
  • 本地代码,直接throw new Error("")
  • 本地代码,reject({message:''})。这里的{}message属性,实际上就是对Errormessage的简单模仿。
  • 不推荐本地reject()一个字符串,推荐:包裹在new Error()里面,或者放在{message:""}里面。

第一组组合,标准版

写接口这种事,还是php更好理解更好用,express写接口总感觉是像过家家一样。当然,为了技术体系的一致性,这里演示如何使用express做接口服务器。

服务器接口

关于接口返回值,苏南大叔觉得,返回的信息有用,就不修改状态码。如果没有用,主动或者被动修改状态码都无所谓。所以,对于自定义错误的情况,苏南大叔倾向于不修改状态码。

npm i express cors --save

server.js:

const express = require("express");
const app = express();
// 跨域,node不考虑,浏览器环境需要..
var cors = require("cors");
app.use(cors());
// Get请求范例
app.get("/get", function (req, res) {
  // console.log(req.query); //获取get请求的参数
  let s = req.query?.s ? req.query.s : "";
  let n = Date.now().toString().slice(-6);
  let tmp = Math.random();
  if (tmp > 0.7) {
    ret = { code: 1, data: { id: n, name: "苏南大叔" + s } };
  } else {
    ret = { code: 0, message: "网络错误" };
    // res.status(500); // 配合客户端!response.ok,throw new Error()使用
  }
  res.send(JSON.stringify(ret));
});
const server = app.listen(process.env.PORT || 3222, () => {
  const port = server.address().port;
  console.log("http://localhost:%d", port);
});

参考文章:

客户端请求 fetch

如果在node端使用fetch的话,还需要额外安装node-fetch。但是,不需要设置cors

npm i node-fetch --save

如果是浏览器环境,可以有选择的在服务器端设置跨域cors。不需要额外安装fetch函数,浏览器自带。

function fetchDataReal() {
  return fetch("http://localhost:3222/get")
    .then(async (response) => {
      if (!response.ok) {
        // throw new Error("Failed to fetch data");
        throw new Error(await response.text());
      }
      return response.json();
    });
    // .catch((error) => {
    //   // 这里的message极有可能就是 Failed to fetch data
    //   // 否则理论上来说,走不到这里
    //   // 这里return的话,对于下一步来说,也是success,不是error,
    //   // react的userequest会得不到错误信息
    //   return { code: -999, message: error.message };
    // });
}
(async () => {
  console.log(await fetchDataReal());
})();

对于这段promise的理解,可以参考:

第二组组合,混乱版

关于自定义错误是不是应该修改状态码的问题,这里有个混乱版。其实也能用。不过,调试完之后的结论就是:不如不发送这个状态码,太混乱。
主要体现在第一个.then的处理之上,这里需要根据response.ok这个值,判断是否抛出异常。如果想拿到这个自定义错误内容,还需要async(response)=>{await response.text()}

接口服务端

// ...
app.get("/get", function (req, res) {
  // console.log(req.query); //获取get请求的参数
  let s = req.query?.p ? req.query.p : "";
  let n = Date.now().toString().slice(-6);
  let tmp = Math.random();
  if (tmp > 0.7) {
    ret = { code: 1, ret: { id: n, name: "苏南大叔" + s } };
  } else if (tmp > 0.6) {
    let tmp2 = Math.random();
    if (tmp2 > 0.5) {
      res.status(500);         // 这里会导致接收端混乱;
      ret = { code: 0, message: "网络错误500状态码" };
    } else {
      ret = { code: 0, message: "网络错误" };
    }
  } else {
    throw new Error("server");
  }
  res.send(JSON.stringify(ret));
});
//...

客户端写法fetch

function fetchDataReal() {
  return fetch("http://localhost:3222/get")
    .then(async (response) => {
      if (!response.ok) {
        // throw new Error(`服务器错误,状态码: ${response.status}`);
        // 主动抛出错误才会被catch
        // 不throw的话,反而会被下边的.json()处理
        throw new Error(await response.text());
      }
      return response.json(); // .text()和.json()一样,是个promise
    })
    .then((data) => {
      return data;
    })
    .catch((error) => {
      return { code: -999, message: error.message }; // 根据实际情况修改
    })
    .finally(() => {
      console.log("这里才有用");
      return "写啥都没用";
    });
}
(async () => {
  console.log(await fetchDataReal());
})();

参考文章:

对照组

有对照才有结论。

对照组一,对比普通promise

请求远程接口的过程,如果像其它普通的非远程fetch的情况的话(直接await),拿到的是个response对象,参考第一个.then的第一个参数response

async function fetchData() {
  return await fetch("http://localhost:3222/get");
}

输出:

Response {
  status: 500,
  statusText: 'Internal Server Error',
  headers: Headers {
    'x-powered-by': 'Express',
    'access-control-allow-origin': '*',
    'content-security-policy': "default-src 'none'",
    'x-content-type-options': 'nosniff',
    'content-type': 'text/html; charset=utf-8',
    'content-length': '977',
    date: 'Tue, 03 Dec 2024 06:38:14 GMT',
    connection: 'keep-alive',
    'keep-alive': 'timeout=5'
  },
  body: ReadableStream { locked: false, state: 'readable', supportsBYOB: true },
  bodyUsed: false,
  ok: false,
  redirected: false,
  type: 'basic',
  url: 'http://localhost:3222/get'
}
这里是个伏笔,后续文章里面有用到这里的结论。

对照组二,axios版本

这里是axios版本的写法,大家看看区别有多大。

纯node版下,需要安装axios:

npm i axios --save
const axios = require('axios');

浏览器版本情况下,可以来这里下载个可用的dist/axios.min.js文件。

<script src='dist/axios.min.js'></script>

最简单的写法:

const axios = require("axios");
(async (p) => {
  let ss = await axios
    .get("http://localhost:3222/get")
    .then((res) => {
      return res.data;
    })
    .catch((err) => {
      return { code: -999, message: err.message };
    });
  console.log(ss);
})();

参考文章:

感觉这个axios的写法,还是要比fetch的写法,要简单一些。

结语

远程数据请求,总是一个老生常谈,还每次都能谈出新花样的问题。更多js经验文章,请参考:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   js