NodeJs及浏览器环境,真实GET请求远程接口方案总结
发布于 作者:苏南大叔 来源:程序如此灵动~本文的内容主要分为两个方面,一个是使用express
做个接口服务,第二个是如何请求这个接口。以前也经常写这方面的内容,这里就是新瓶装老酒。这里单独成篇一篇内容,以后就直接引用这篇文章,少写内容了。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0
,express@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.ok
和response.text()
再次抛出这个异常。 - 如果客户端是在纯
node
程序里面请求的话,并不需要服务器端设置跨域cors
。浏览器环境下的话,就看情况设置跨域了。
Error兼容
以下几种情况,是会被本地代码视为被catch()
的对象,但是不一定是个Error
,也可能是个仿Error
的{message:''}
。
- 服务器端修改状态码非200,本地解析
response
,根据!.ok
,throw a Error("")
。如果这里需要明确获得服务器消息的话,这里还需要await response.text()
。 - 本地代码,直接
throw new Error("")
。 - 本地代码,
reject({message:''})
。这里的{}
带message
属性,实际上就是对Error
的message
的简单模仿。 - 不推荐本地
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
经验文章,请参考:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。