Node.js如何使用http、url和querystring模块构建长尾词查询后端服务器?
- 内容介绍
- 文章标签
- 相关推荐
本文共计4795个文字,预计阅读时间需要20分钟。
目录+前言+一、创建服务器+二、返回响应数据+三、返回复杂对象数据+四、返回HTML文档数据+五、设置响应头和状态码+六、实现路由接口+七、创建简易路由应用+八、处理URL+九、URL格式转换+十、URL路径拼接
目录
- 前言
- 一、创建服务器
- 二、返回响应数据
- 返回复杂对象数据
- 返回html文档数据
- 三、设置响应头和状态码
- 四、实现路由接口
- 创建简易路由应用
- 五、处理URL
- URL格式转换
- URL路径拼接
- 正确转换文件路径
- 转换为Options Url对象
- 六、跨域处理
- 后端设置跨域
- jsonp接口
- 七、Node作为中间层使用
- 模拟get请求(转发跨域数据)
- 模拟post请求(服务器提交)
- 八、使用Node实现爬虫
前言
这一节我们去学习NodeJs的内置模块:localhost:3000/时会发现浏览器一直在转圈圈加载
注意: 直接在浏览器地址栏里访问服务器接口,相当于是使用
get请求访问服务器接口(并且没有跨域限制)
这是因为我们并没有在我们创建的node服务器中返回任何内容
二、返回响应数据
我们可以通过我们定义的res(response对象)参数向客户端发送响应内容:
const localhost:3000/list,控制台会打印出:
可以看到我们访问的/list路径确实被打印出来了,但怎么还打印了一个/favicon.ico呢?
这其实是浏览器在访问一个域名时会自动访问该域名下的/favicon.ico静态文件,来作为网页标签栏的小图标,所以我们的服务器才会打印出/favicon.ico
如果是普通的ajax调用接口是不会出现这种情况的,这里我们是为了方便,直接使用浏览器访问接口来进行演示,所以才出现这种请求,我们可以简单做一下处理:
const 127.0.0.1:3000").pathname; // 传一个参数用法 // const myUrl = new URL("127.0.0.1:3000" + req.url).pathname; console.log(new URL(req.url, "127.0.0.1:3000")); res.writeHead(renderStatus(myUrl), { "Content-Type": "application/json", }); res.end(renderContent(myUrl)); }); server.listen(3000, () => { // 服务器端口号为3000 console.log("服务器启动啦!"); });
全局的构造函数UR可以将完整的 url地址转换成url对象(WHATWG URL标准的对象)
我们可以对其传递两个参数,第一个是用户请求的路径(路由),第二个参数是地址的根域名(我们这里是本地启动的服务器,根域名为
127.0.0.1:3000)
也可以直接传递一个参数,该参数是带有域名的完整
url地址
当我们访问localhost:3000/api/about?name=ailjx时server.js会打印出:
URL { href: '127.0.0.1:3000/api/about?name=ailjx', origin: '127.0.0.1:3000', protocol: '127.0.0.1:3000").searchParams; // 使用get方法获取指定的值 console.log(query.get("name")); // ailjx
我们还可以从组成部分构造 URL 并获取构造的字符串:
const myURL = new URL("www.baidu.com"); myURL.port = "443"; myURL.pathname = "/ad/index.html"; myURL.search = "?id=8&name=mouse"; myURL.hash = "#tag=110"; // 获取构造的 URL 字符串,请使用href属性访问器 console.log(myURL.href); // www.baidu.com/ad/index.html?id=8&name=mouse#tag=110
或者:
const pathname = '/a/b/c'; const search = '?d=e'; const hash = '#fgh'; const myURL = new URL(`example.org${pathname}${search}${hash}`); console.log(myURL.href);
使用url.format方法可以自定义序列化url字符串,format方法接收两个参数:
new URL返回的一个WHATWG URL格式的对象- 配置对象:
fragment:序列化的网址字符串是否包含片段,默认为trueauth:序列化的网址字符串是否包含用户名和密码,默认为trueunicode:是否将出现在URL字符串的主机组件中的Unicode字符直接编码而不是Punycode编码,默认是falsesearch:序列化的网址字符串是否包含搜索查询(参数),默认为true
const myURL = new URL( "username:password@URL路径序列化测试?name=ailjx#foo" ); console.log( url.format(myURL, { fragment: false, // 不显示片段(#foo) unicode: true, // 不转化Unicode字符(中文字符) auth: false, // 不包含用户名和密码(username:password) search: false, // 不显示参数(?name=ailjx) }) ); // 打印结果: 'url路径序列化测试/'
旧版Node使用parse和format处理URL:
注意:旧版的
parse方法官方表示已弃用,format方法在新版中使用方式有所更改
const localhost:3000/api/about?name=ailjx时server.js会打印出:
Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: '?name=ailjx', query: 'name=ailjx', pathname: '/api/about', path: '/api/about?name=ailjx', href: '/api/about?name=ailjx' }
上面Url对象里 query 是url的参数部分,默认是字符串的格式,可以给parse方法传递第二个参数,将其转换成对象格式:
url.parse(req.url,true)
Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: '?name=ailjx', query: [Object: null prototype] { name: 'ailjx' }, pathname: '/api/about', path: '/api/about?name=ailjx', href: '/api/about?name=ailjx' }
这时通过url.parse(req.url, true).query.name就可以拿到ailjx这个参数
与parse方法相反的有一个format方法,它能将一个 url对象转换成url地址 :
const url = require("url"); const urlObject = { protocol: "www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110
URL路径拼接
URL构造函数,传递两个字符串路径时能够实现路径的拼接:
let myURL = new URL('Example.com/', 'example.org/'); // example.com/ myURL = new URL('Example.com/', 'example.org/'); // example.com/ myURL = new URL('foo://Example.com/', 'example.org/'); // foo://Example.com/ myURL = new URL('example.org/'); // example.com/ myURL = new URL('example.org/'); // example.org/Example.com/ myURL = new URL('foo:Example.com/', 'example.org/'); // foo:Example.com/
旧版Node使用resolve方法拼接路径:
注意:旧版node的
resolve方法官方表示已弃用
const url = require('url') var a = url.resolve("/one/two/three", "four"); // /one/two/four // var a = url.resolve("/one/two/three/", "four"); // /one/two/three/four // var a = url.resolve("/one/two/three", "/four"); // /four // var a = url.resolve("/one/two/three/", "/four"); // /four var b = url.resolve("example.com/", "/one"); // var b = url.resolve("example.com/", "one"); // var b = url.resolve("example.com", "one"); // var b = url.resolve("example.com", "/one"); // 以上b的结果都是:example.com/one var c = url.resolve("example.com/one", "two"); // var c = url.resolve("example.com/one", "/two"); // var c = url.resolve("example.com/one/", "/two"); // var c = url.resolve("example.com/one/a/b", "/two"); // 以上c的结果都是:example.com/two var d = url.resolve("example.com/one/", "two"); // example.com/one/two var e = url.resolve("example.com/one/aaa", "example.com/one/two"); // var e = url.resolve("/one/aaa", "example.com/one/two"); // 以上e的结果都是:example.com/one/two
resolve方法并不是简单的将两个路径直接拼接在一起,而是具有它自己的一些拼接规则:
- 如果第二个路径(接收的第二个参数)开头前带
/,则将直接用第二个路径替代第一个路径的路由部分(不会替代第一个路径的根域名,如上面的最后一个变量c所示:/two替代了/one/a/b) - 如果第二个路径开头前不带
/,第一个路径结尾处不带/,则第二个路径将会替代第一个路径的最后一个路由,如上边的第一个变量a和第一个变量c - 第二个路径开头前不带
/,第一个路径结尾处带/,则直接将第二个路径拼接在第一个路径后面,如上边的第变量d和第二个变量a - 如果第二个路径包含根域名(
xxx),则直接以第二个路径为主(第一个路径失效) 处理URL路径参数
注意:
querystring方法官方表示已弃用
NodeJS有一个内置模块querystring,它里面的parse和stringify方法可以帮助我们快速处理URL上的形如id=8&name=mouse的参数:
const querystring = require("querystring"); var qs = "id=8&name=Ailjx"; // parse:路径将参数转化为对象 var parsed = querystring.parse(qs); console.log(parsed.id, parsed.name); // 8 Ailjx var qo = { x: 3, y: 4, }; //stringify:将对象转化为路径参数 var parsed = querystring.stringify(qo); console.log(parsed); // x=3&y=4
querystring中还有一对能够转义特殊字符的方法: escape/unescape:
const querystring = require("querystring"); var str = 'ns"--'; // escape:将特殊字符转义 var escaped = querystring.escape(str); console.log(escaped); //ns%22-- // unescape:恢复转义的特殊字符 console.log(querystring.unescape(escaped)); // ns"--
对于特殊字符的转义在一些特殊情况下特别重要,例如我们通过用户传递的参数来向mysql数据库查询数据,我们为了防止用户传递的参数与sql语句发送冲突,就可以对该参数进行转义,以防止sql注入
例如有一条含有用户传递的param参数的sql语句:
let sql = `select * from users where name = "${param}" and del_status=1`
上面的sql语句在正常情况下是只能查询到一条数据,如:
// let param = 'Ailjx' ,对应的sql语句如下: select * from users where name = "Ailjx" and del_status=1
但当param与sql语句冲突时:
// let param = 'ns"--',对应的sql语句如下: select * from tb_nature where nature = "ns"-- " and del_status=1
可以看到del_status被参数中的--注释掉了,失去了作用,这时这条sql能查询到多条数据,这就是sql注入的危害,也就是我们需要转义特殊字符的原因
正确转换文件路径
url模块针对转换文件路径提供了单独的fileURLToPath和pathToFileURL方法,它们不仅能正确进行文件路径的转换而且能自动适配不同的操作系统
fileURLToPath该方法能够正确将文件网址url转换成文件路径:
const { fileURLToPath } = require("url"); console.log(new URL("file:///C:/path/").pathname); // 获得错误路径:/C:/path/ console.log(fileURLToPath("file:///C:/path/")); // 获得正确路径:C:\path\ (Windows) console.log(new URL("file://nas/foo.txt").pathname); // 获得错误路径:/foo.txt console.log(fileURLToPath("file://nas/foo.txt")); // 获得正确路径: \\nas\foo.txt (Windows) console.log(new URL("file://c://你好.txt").pathname); // 获得错误路径:/c://%E4%BD%A0%E5%A5%BD.txt console.log(fileURLToPath("file://c://你好.txt")); // 获得正确路径: c:\\你好.txt (Windows)
pathToFileURL方法,能够将文件路径转换成文件网址url对象:
const { pathToFileURL } = require("url"); console.log(new URL("/foo#1", "file:").href); // 错误: file:///foo#1 console.log(pathToFileURL("/foo#1").href); // 正确: file:///D:/foo%231 console.log(new URL("/some/path%.c", "file:").href); // 错误: file:///some/path%.c console.log(pathToFileURL("/some/path%.c").href); // 正确: file:///D:/some/path%25.c
转换为Options Url对象
在不做处理的情况下,前后端交互会出现 定义一个简单的服务器: const localhost:3000/api/user')
.then(res => res.json())
.then(res => console.log(res))
</script>
</body>
打开该 这种问题有多种解决方案: 后端可以直接在响应头里设置跨域,而前端不需要做额外的操作 我们下载一个urlToHttpOptions方法可以将new URL返回的WHATWG URL对象转换成a:b@測試?abc#foo');
console.log(urlToHttpOptions(myURL));
/*
{
protocol: 'a:b@xn--g6w251d/?abc#foo',
auth: 'a:b'
}
*/
六、跨域处理
CORS跨域的问题,如:server.jshtml文件,可以看到果然出现了CORS跨域的报错jsonp方式进行交互(前后端都需要进行相应修改)vue,react等这些框架中经常使用)后端设置跨域
vscode的Live Server插件,用于在线运行html文件:
右键html文件选择Open with Live Server:
打开后发现前端html的在线运行地址为127.0.0.1:5500,我们在后端返回数据的响应头里允许该地址访问即可:
修改一下上边的server.js
const 127.0.0.1:5500访问 "Access-Control-Allow-Origin": "127.0.0.1:5500", // "Access-Control-Allow-Origin": "*", 也可以使用'*',代表允许所有地址访问 }); switch (urlObj.pathname) { case "/api/user": // 模拟数据 const userinfo = { name: "Ailjx", }; res.end(JSON.stringify(userinfo)); break; default: res.end("404"); break; } }); server.listen(3000, () => { console.log("服务器启动啦!"); });
这时前端就能正常调用该接口了:
jsonp接口
我们知道html的script标签可以引入js文件,并且重要的是使用script标签引入js没有跨域的要求,所以我们可以在script标签的src属性中调用我们的接口来获取后端返回的数据
前端处理:
<body> <div>jsonp接口测试</div> <div> name: <b id="myName"></b> </div> <script> // 定义一个接收后端返回数据的函数 function getUser(params) { const myName = document.getElementById('myName') // 可以做一些操作 myName.innerText = params.name } // 创建一个script标签 const myScript = document.createElement('script') // script标签的src内调用接口,需要将我们定义的接收数据的函数名传递给后端 myScript.src = 'localhost:3000/api/user?cb=getUser' // 向文档内插入该script标签 document.body.appendChild(myScript) </script> </body>
或者直接用script调用后端接口:
<body> <div>jsonp接口测试</div> <div> name: <b id="myName"></b> </div> <script> // // 定义一个接收后端返回数据的函数 function getUser(params) { const myName = document.getElementById('myName') myName.innerText = params.name } </script> <!-- 调用后端接口 --> <script src="localhost:3000/api/user?cb=getUser"></script> </body>
后端处理:
const localhost:3000/api/user?cb=getUser"></script>的效果如下 --> <script> getUser({ name: "Ailjx", }) </script>
七、Node作为中间层使用
上面我们使用node搭建了后端服务器,使其作为服务端运行,但其实node还能当作客户端反过来去调用其它服务端的接口,这使得node成为了一个中间层
因为跨域只是浏览器的限制,服务端之间的通信并不存在跨域的问题,这样我们就能借助node去调用第三方具有跨域限制的接口,这是将node作为中间层的一个非常实用的功能
模拟get请求(转发跨域数据)
在猫眼电影网上随便找了一个带有跨域的接口,我们直接调用时会报CORS跨域问题:
<h1>使用node模拟get请求</h1> <script> fetch('i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E8%A5%BF%E5%8D%8E&ci=936&channelId=4') .then(res => res.json()) .then(res => { console.log(res); }) </script>
这时我们可以利用node帮我们去请求这个接口的数据:
const 127.0.0.1:5500", }); switch (urlObj.pathname) { case "/api/maoyan": // 我们定义的i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E8%A5%BF%E5%8D%8E&ci=936&channelId=4", (res) => { // localhost:3000/api/maoyan') .then(res => res.json()) .then(res => { console.log(res); }) </script>
这里node即作为服务端给我们提供接口/api/maoyan,又充当了一下客户端去调用猫眼的接口,这样我们就绕过了猫眼的跨域限制获取了它的数据
模拟post请求(服务器提交)
使用 我们使用 我们在一个文件夹内打开终端,先生成 npm init
安装 npm i cheerio
文件夹内创建我们的服务器文件 const i.maoyan.com)的写法,你也可以直接使用 到此这篇关于Node.js 搭建后端服务器内置模块( http+url+querystring 的使用)的文章就介绍到这了,更多相关Node.js 后端搭建内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!node模拟post请求需要使用127.0.0.1:5500",
});
switch (urlObj.pathname) {
case "/api/xiaomiyoumin":
// m.xiaomiyoupin.com/mtop/market/search/placeHolder"
// 这个接口调用时需要传“[{}, { baseParam: { ypClient: 1 } }]”这样一个参数才能返回数据
// 配置Options Url请求对象
const options = {
// 域名
hostname: "m.xiaomiyoupin.com",
// 接口端口号,443代表localhost:3000/api/xiaomiyoumin')
.then(res => res.json())
.then(res => {
console.log(res);
})
</script>
</body>
八、使用Node实现爬虫
node的一个cheerio包也可以实现爬虫功能,如我们爬取猫眼移动端i.maoyan.com首页的一些数据:package.json文件:cheerio:server.js:i.maoyan.com接口时获取不到数据,可能是我们浏览器上的猫眼网进入了验证操作,我们在访问我们服务器的这个浏览器上打开i.maoyan.com进行验证一下,之后我们就能请求到i.maoyan.com的html文档内容了
本文共计4795个文字,预计阅读时间需要20分钟。
目录+前言+一、创建服务器+二、返回响应数据+三、返回复杂对象数据+四、返回HTML文档数据+五、设置响应头和状态码+六、实现路由接口+七、创建简易路由应用+八、处理URL+九、URL格式转换+十、URL路径拼接
目录
- 前言
- 一、创建服务器
- 二、返回响应数据
- 返回复杂对象数据
- 返回html文档数据
- 三、设置响应头和状态码
- 四、实现路由接口
- 创建简易路由应用
- 五、处理URL
- URL格式转换
- URL路径拼接
- 正确转换文件路径
- 转换为Options Url对象
- 六、跨域处理
- 后端设置跨域
- jsonp接口
- 七、Node作为中间层使用
- 模拟get请求(转发跨域数据)
- 模拟post请求(服务器提交)
- 八、使用Node实现爬虫
前言
这一节我们去学习NodeJs的内置模块:localhost:3000/时会发现浏览器一直在转圈圈加载
注意: 直接在浏览器地址栏里访问服务器接口,相当于是使用
get请求访问服务器接口(并且没有跨域限制)
这是因为我们并没有在我们创建的node服务器中返回任何内容
二、返回响应数据
我们可以通过我们定义的res(response对象)参数向客户端发送响应内容:
const localhost:3000/list,控制台会打印出:
可以看到我们访问的/list路径确实被打印出来了,但怎么还打印了一个/favicon.ico呢?
这其实是浏览器在访问一个域名时会自动访问该域名下的/favicon.ico静态文件,来作为网页标签栏的小图标,所以我们的服务器才会打印出/favicon.ico
如果是普通的ajax调用接口是不会出现这种情况的,这里我们是为了方便,直接使用浏览器访问接口来进行演示,所以才出现这种请求,我们可以简单做一下处理:
const 127.0.0.1:3000").pathname; // 传一个参数用法 // const myUrl = new URL("127.0.0.1:3000" + req.url).pathname; console.log(new URL(req.url, "127.0.0.1:3000")); res.writeHead(renderStatus(myUrl), { "Content-Type": "application/json", }); res.end(renderContent(myUrl)); }); server.listen(3000, () => { // 服务器端口号为3000 console.log("服务器启动啦!"); });
全局的构造函数UR可以将完整的 url地址转换成url对象(WHATWG URL标准的对象)
我们可以对其传递两个参数,第一个是用户请求的路径(路由),第二个参数是地址的根域名(我们这里是本地启动的服务器,根域名为
127.0.0.1:3000)
也可以直接传递一个参数,该参数是带有域名的完整
url地址
当我们访问localhost:3000/api/about?name=ailjx时server.js会打印出:
URL { href: '127.0.0.1:3000/api/about?name=ailjx', origin: '127.0.0.1:3000', protocol: '127.0.0.1:3000").searchParams; // 使用get方法获取指定的值 console.log(query.get("name")); // ailjx
我们还可以从组成部分构造 URL 并获取构造的字符串:
const myURL = new URL("www.baidu.com"); myURL.port = "443"; myURL.pathname = "/ad/index.html"; myURL.search = "?id=8&name=mouse"; myURL.hash = "#tag=110"; // 获取构造的 URL 字符串,请使用href属性访问器 console.log(myURL.href); // www.baidu.com/ad/index.html?id=8&name=mouse#tag=110
或者:
const pathname = '/a/b/c'; const search = '?d=e'; const hash = '#fgh'; const myURL = new URL(`example.org${pathname}${search}${hash}`); console.log(myURL.href);
使用url.format方法可以自定义序列化url字符串,format方法接收两个参数:
new URL返回的一个WHATWG URL格式的对象- 配置对象:
fragment:序列化的网址字符串是否包含片段,默认为trueauth:序列化的网址字符串是否包含用户名和密码,默认为trueunicode:是否将出现在URL字符串的主机组件中的Unicode字符直接编码而不是Punycode编码,默认是falsesearch:序列化的网址字符串是否包含搜索查询(参数),默认为true
const myURL = new URL( "username:password@URL路径序列化测试?name=ailjx#foo" ); console.log( url.format(myURL, { fragment: false, // 不显示片段(#foo) unicode: true, // 不转化Unicode字符(中文字符) auth: false, // 不包含用户名和密码(username:password) search: false, // 不显示参数(?name=ailjx) }) ); // 打印结果: 'url路径序列化测试/'
旧版Node使用parse和format处理URL:
注意:旧版的
parse方法官方表示已弃用,format方法在新版中使用方式有所更改
const localhost:3000/api/about?name=ailjx时server.js会打印出:
Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: '?name=ailjx', query: 'name=ailjx', pathname: '/api/about', path: '/api/about?name=ailjx', href: '/api/about?name=ailjx' }
上面Url对象里 query 是url的参数部分,默认是字符串的格式,可以给parse方法传递第二个参数,将其转换成对象格式:
url.parse(req.url,true)
Url { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: '?name=ailjx', query: [Object: null prototype] { name: 'ailjx' }, pathname: '/api/about', path: '/api/about?name=ailjx', href: '/api/about?name=ailjx' }
这时通过url.parse(req.url, true).query.name就可以拿到ailjx这个参数
与parse方法相反的有一个format方法,它能将一个 url对象转换成url地址 :
const url = require("url"); const urlObject = { protocol: "www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110
URL路径拼接
URL构造函数,传递两个字符串路径时能够实现路径的拼接:
let myURL = new URL('Example.com/', 'example.org/'); // example.com/ myURL = new URL('Example.com/', 'example.org/'); // example.com/ myURL = new URL('foo://Example.com/', 'example.org/'); // foo://Example.com/ myURL = new URL('example.org/'); // example.com/ myURL = new URL('example.org/'); // example.org/Example.com/ myURL = new URL('foo:Example.com/', 'example.org/'); // foo:Example.com/
旧版Node使用resolve方法拼接路径:
注意:旧版node的
resolve方法官方表示已弃用
const url = require('url') var a = url.resolve("/one/two/three", "four"); // /one/two/four // var a = url.resolve("/one/two/three/", "four"); // /one/two/three/four // var a = url.resolve("/one/two/three", "/four"); // /four // var a = url.resolve("/one/two/three/", "/four"); // /four var b = url.resolve("example.com/", "/one"); // var b = url.resolve("example.com/", "one"); // var b = url.resolve("example.com", "one"); // var b = url.resolve("example.com", "/one"); // 以上b的结果都是:example.com/one var c = url.resolve("example.com/one", "two"); // var c = url.resolve("example.com/one", "/two"); // var c = url.resolve("example.com/one/", "/two"); // var c = url.resolve("example.com/one/a/b", "/two"); // 以上c的结果都是:example.com/two var d = url.resolve("example.com/one/", "two"); // example.com/one/two var e = url.resolve("example.com/one/aaa", "example.com/one/two"); // var e = url.resolve("/one/aaa", "example.com/one/two"); // 以上e的结果都是:example.com/one/two
resolve方法并不是简单的将两个路径直接拼接在一起,而是具有它自己的一些拼接规则:
- 如果第二个路径(接收的第二个参数)开头前带
/,则将直接用第二个路径替代第一个路径的路由部分(不会替代第一个路径的根域名,如上面的最后一个变量c所示:/two替代了/one/a/b) - 如果第二个路径开头前不带
/,第一个路径结尾处不带/,则第二个路径将会替代第一个路径的最后一个路由,如上边的第一个变量a和第一个变量c - 第二个路径开头前不带
/,第一个路径结尾处带/,则直接将第二个路径拼接在第一个路径后面,如上边的第变量d和第二个变量a - 如果第二个路径包含根域名(
xxx),则直接以第二个路径为主(第一个路径失效) 处理URL路径参数
注意:
querystring方法官方表示已弃用
NodeJS有一个内置模块querystring,它里面的parse和stringify方法可以帮助我们快速处理URL上的形如id=8&name=mouse的参数:
const querystring = require("querystring"); var qs = "id=8&name=Ailjx"; // parse:路径将参数转化为对象 var parsed = querystring.parse(qs); console.log(parsed.id, parsed.name); // 8 Ailjx var qo = { x: 3, y: 4, }; //stringify:将对象转化为路径参数 var parsed = querystring.stringify(qo); console.log(parsed); // x=3&y=4
querystring中还有一对能够转义特殊字符的方法: escape/unescape:
const querystring = require("querystring"); var str = 'ns"--'; // escape:将特殊字符转义 var escaped = querystring.escape(str); console.log(escaped); //ns%22-- // unescape:恢复转义的特殊字符 console.log(querystring.unescape(escaped)); // ns"--
对于特殊字符的转义在一些特殊情况下特别重要,例如我们通过用户传递的参数来向mysql数据库查询数据,我们为了防止用户传递的参数与sql语句发送冲突,就可以对该参数进行转义,以防止sql注入
例如有一条含有用户传递的param参数的sql语句:
let sql = `select * from users where name = "${param}" and del_status=1`
上面的sql语句在正常情况下是只能查询到一条数据,如:
// let param = 'Ailjx' ,对应的sql语句如下: select * from users where name = "Ailjx" and del_status=1
但当param与sql语句冲突时:
// let param = 'ns"--',对应的sql语句如下: select * from tb_nature where nature = "ns"-- " and del_status=1
可以看到del_status被参数中的--注释掉了,失去了作用,这时这条sql能查询到多条数据,这就是sql注入的危害,也就是我们需要转义特殊字符的原因
正确转换文件路径
url模块针对转换文件路径提供了单独的fileURLToPath和pathToFileURL方法,它们不仅能正确进行文件路径的转换而且能自动适配不同的操作系统
fileURLToPath该方法能够正确将文件网址url转换成文件路径:
const { fileURLToPath } = require("url"); console.log(new URL("file:///C:/path/").pathname); // 获得错误路径:/C:/path/ console.log(fileURLToPath("file:///C:/path/")); // 获得正确路径:C:\path\ (Windows) console.log(new URL("file://nas/foo.txt").pathname); // 获得错误路径:/foo.txt console.log(fileURLToPath("file://nas/foo.txt")); // 获得正确路径: \\nas\foo.txt (Windows) console.log(new URL("file://c://你好.txt").pathname); // 获得错误路径:/c://%E4%BD%A0%E5%A5%BD.txt console.log(fileURLToPath("file://c://你好.txt")); // 获得正确路径: c:\\你好.txt (Windows)
pathToFileURL方法,能够将文件路径转换成文件网址url对象:
const { pathToFileURL } = require("url"); console.log(new URL("/foo#1", "file:").href); // 错误: file:///foo#1 console.log(pathToFileURL("/foo#1").href); // 正确: file:///D:/foo%231 console.log(new URL("/some/path%.c", "file:").href); // 错误: file:///some/path%.c console.log(pathToFileURL("/some/path%.c").href); // 正确: file:///D:/some/path%25.c
转换为Options Url对象
在不做处理的情况下,前后端交互会出现 定义一个简单的服务器: const localhost:3000/api/user')
.then(res => res.json())
.then(res => console.log(res))
</script>
</body>
打开该 这种问题有多种解决方案: 后端可以直接在响应头里设置跨域,而前端不需要做额外的操作 我们下载一个urlToHttpOptions方法可以将new URL返回的WHATWG URL对象转换成a:b@測試?abc#foo');
console.log(urlToHttpOptions(myURL));
/*
{
protocol: 'a:b@xn--g6w251d/?abc#foo',
auth: 'a:b'
}
*/
六、跨域处理
CORS跨域的问题,如:server.jshtml文件,可以看到果然出现了CORS跨域的报错jsonp方式进行交互(前后端都需要进行相应修改)vue,react等这些框架中经常使用)后端设置跨域
vscode的Live Server插件,用于在线运行html文件:
右键html文件选择Open with Live Server:
打开后发现前端html的在线运行地址为127.0.0.1:5500,我们在后端返回数据的响应头里允许该地址访问即可:
修改一下上边的server.js
const 127.0.0.1:5500访问 "Access-Control-Allow-Origin": "127.0.0.1:5500", // "Access-Control-Allow-Origin": "*", 也可以使用'*',代表允许所有地址访问 }); switch (urlObj.pathname) { case "/api/user": // 模拟数据 const userinfo = { name: "Ailjx", }; res.end(JSON.stringify(userinfo)); break; default: res.end("404"); break; } }); server.listen(3000, () => { console.log("服务器启动啦!"); });
这时前端就能正常调用该接口了:
jsonp接口
我们知道html的script标签可以引入js文件,并且重要的是使用script标签引入js没有跨域的要求,所以我们可以在script标签的src属性中调用我们的接口来获取后端返回的数据
前端处理:
<body> <div>jsonp接口测试</div> <div> name: <b id="myName"></b> </div> <script> // 定义一个接收后端返回数据的函数 function getUser(params) { const myName = document.getElementById('myName') // 可以做一些操作 myName.innerText = params.name } // 创建一个script标签 const myScript = document.createElement('script') // script标签的src内调用接口,需要将我们定义的接收数据的函数名传递给后端 myScript.src = 'localhost:3000/api/user?cb=getUser' // 向文档内插入该script标签 document.body.appendChild(myScript) </script> </body>
或者直接用script调用后端接口:
<body> <div>jsonp接口测试</div> <div> name: <b id="myName"></b> </div> <script> // // 定义一个接收后端返回数据的函数 function getUser(params) { const myName = document.getElementById('myName') myName.innerText = params.name } </script> <!-- 调用后端接口 --> <script src="localhost:3000/api/user?cb=getUser"></script> </body>
后端处理:
const localhost:3000/api/user?cb=getUser"></script>的效果如下 --> <script> getUser({ name: "Ailjx", }) </script>
七、Node作为中间层使用
上面我们使用node搭建了后端服务器,使其作为服务端运行,但其实node还能当作客户端反过来去调用其它服务端的接口,这使得node成为了一个中间层
因为跨域只是浏览器的限制,服务端之间的通信并不存在跨域的问题,这样我们就能借助node去调用第三方具有跨域限制的接口,这是将node作为中间层的一个非常实用的功能
模拟get请求(转发跨域数据)
在猫眼电影网上随便找了一个带有跨域的接口,我们直接调用时会报CORS跨域问题:
<h1>使用node模拟get请求</h1> <script> fetch('i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E8%A5%BF%E5%8D%8E&ci=936&channelId=4') .then(res => res.json()) .then(res => { console.log(res); }) </script>
这时我们可以利用node帮我们去请求这个接口的数据:
const 127.0.0.1:5500", }); switch (urlObj.pathname) { case "/api/maoyan": // 我们定义的i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E8%A5%BF%E5%8D%8E&ci=936&channelId=4", (res) => { // localhost:3000/api/maoyan') .then(res => res.json()) .then(res => { console.log(res); }) </script>
这里node即作为服务端给我们提供接口/api/maoyan,又充当了一下客户端去调用猫眼的接口,这样我们就绕过了猫眼的跨域限制获取了它的数据
模拟post请求(服务器提交)
使用 我们使用 我们在一个文件夹内打开终端,先生成 npm init
安装 npm i cheerio
文件夹内创建我们的服务器文件 const i.maoyan.com)的写法,你也可以直接使用 到此这篇关于Node.js 搭建后端服务器内置模块( http+url+querystring 的使用)的文章就介绍到这了,更多相关Node.js 后端搭建内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!node模拟post请求需要使用127.0.0.1:5500",
});
switch (urlObj.pathname) {
case "/api/xiaomiyoumin":
// m.xiaomiyoupin.com/mtop/market/search/placeHolder"
// 这个接口调用时需要传“[{}, { baseParam: { ypClient: 1 } }]”这样一个参数才能返回数据
// 配置Options Url请求对象
const options = {
// 域名
hostname: "m.xiaomiyoupin.com",
// 接口端口号,443代表localhost:3000/api/xiaomiyoumin')
.then(res => res.json())
.then(res => {
console.log(res);
})
</script>
</body>
八、使用Node实现爬虫
node的一个cheerio包也可以实现爬虫功能,如我们爬取猫眼移动端i.maoyan.com首页的一些数据:package.json文件:cheerio:server.js:i.maoyan.com接口时获取不到数据,可能是我们浏览器上的猫眼网进入了验证操作,我们在访问我们服务器的这个浏览器上打开i.maoyan.com进行验证一下,之后我们就能请求到i.maoyan.com的html文档内容了

