简介
- node http接口
- 实现即时通信
- 网络爬虫
1. 实现 Http 接口
- 实现一个简单的 http 请求
- http 请求有跨域
- 预检请求 options,设置响应头
- credentail 请求,携带 cookie 信息
实现简单 http
``javascript // index.html // 使用 axios 请求 /api/ const res = await axios.get("/api/user") document.writeln(
Response: ${JSON.stringify(res.data)}`)
// index.js const http = require(“http”); const fs = require(“fs”); http.createServer((req, res) => { const { method, url } = req; if (method == “GET” && url == “/api/user”) { res.setHeader(“Content-Type”, “application/json”); res.end(JSON.stringify({ name: “qiji”, age: 25 })); } }).listen(3000);
<a name="lGeNX"></a>
#### http 跨域
将上面的代码更改一下,这里面让页面和服务地址使用不同的地址(页面:localhost:3000,服务:localhost:4000),产生跨域。
```javascript
// index.html
(async () => {
axios.defaults.baseURL = 'http://localhost:4000'
const res = await axios.get("/api/user");
document.writeln(`Response : ${JSON.stringify(res.data)}`)
})()
//新增一个 proxy.js,启动一个 express 作为服务端地址
const express = require('express')
const app = express()
// __dirname 代码所在的目录
app.use(express.static(__dirname + '/'))
app.listen(3000)
// index.js
// 端口更改为 4000
.listen(4000);
结果如图:
跨域的解决方法
- JSONP:使用 script 标签加载 js 资源不受同源策略限制(现在没人用了吧……)。
设置 CORS
// 属于符合 w3c 的真正意义解决跨域问题
res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000")
代理 ```javascript // index.js / api.js 都不需要设置任何的跨域设置 // api.js .listen(4000)
// index.html
(async () => {
axios.defaults.baseURL = ‘http://localhost:3000‘
const res = await axios.get(“/api/user”)
document.writeln(Response : ${JSON.stringify(res.data)}
)
})()
// proxy.js // node 中可以使用很多种代理服务器,这里选择一种 const proxy = require(‘http-proxy-middleware’)
app.use(‘/api’, proxy({ target: ‘http://localhost:4000‘, changeOrigin: false }));
<a name="0kLRb"></a>
#### 预检请求
预检不成功的请求无法正常发出,不论跨域与否。
```javascript
// index.html
// const res = await axios.get("/api/user");
const res = await axios.post("/api/user", {
headers: {
'X-Token': 'luelue'
}
})
// api.js
// 设置预检 preflight
else if (method == 'OPTIONS' && url == "/api/user") {
res.writeHead(200, {
'Access-Control-Allow-Origin': "http://localhost:3000",
'Access-Control-Allow-Headers': "X-Token,Content-Type",
"Access-Control-Allow-Methods": "PUT"
})
res.end()
}
设置 cookie
一般来说,ajax 正常是不写 cookie 的。
// 关键代码
res.setHeader("Access-Control-Allow-Credentials", 'true')
res.setHeader("Set-Cookie", 'cookie=test')
2. 实现一个即时通信(Socket、Http、Socketio)
即时通信(scoket、http、socketio)代码 demo
socket 实现
《node深入浅出》这本书中的首例,很好的演示了 node 的作用
原理:Net模块提供一个异步API能够创建基于流的TCP服务器,客户端与服务器建立连接后,服务器可以获得一个全双工Socket对象,服务器可以保存Socket对象列表,在接收某客户端消息时,推送给其他客户端。
延伸:TCP/IP 协议,UDP 协议
const net = require('net')
const chatServer = net.createServer()
const clientList = []
// 建立链接后 监听所有访问 9000 的用户,然后,分发他们输入的消息
chatServer.on("connection", client => {
client.write("Hi/n")
clientList.push(client)
client.on('data', data => {
console.log('receive:', data.toString());
clientList.forEach(v => {
v.write(data)
})
})
})
chatServer.listen(9000)
如果其他用户想链接这个 socket 端口,只需要 telnet localhost 9000
。
http 实现
在没有 websocket 的协议时,客户端只能用 http 长轮询的方式获取服务端信息,其实 http 是基于 TCP 协议,如果只是作为通信,携带了大量的无关信息显得冗余,很多时候聊天软件都是基于 UDP 协议,数据量小。
简单来说 http 的方式就是一个一个的请求发过去,返回过来新的数据。
客户端代码:
// vue
<div id="app">
<input v-model="message" /> <button v-on:click="send">发送</button>
<button v-on:click="clear">清空</button>
<div v-for="item in list">{{item}}</div>
</div>
// 设置端口
const host = "http://localhost:3000";
var app = new Vue({
el: "#app",
data: {
list: [],
message: "Hello Vue!"
},
methods: {
// 操作都是一个一个的 requset 请求
send: async function() {
let res = await axios.post(host + "/send", {
message: this.message
});
this.list = res.data;
},
clear: async function() {
let res = await axios.post(host + "/clear");
this.list = res.data;
}
},
mounted: function() {
// 因为 http 天然不能给客户端推数据,这也是 http 的不足,所以需要定时获取。
setInterval(async () => {
const res = await axios.get(host + "/list");
this.list = res.data;
}, 1000);
}
});
服务端代码:
const express = require('express')
const app = express()
const bodyParser = require('body-parser'); // bodyparser 中间件
const path = require('path')
app.use(bodyParser.json());
const list = ['历史记录 1', '历史记录 2']
app.get('/', (req, res) => {
// 绝对路径,使用 path 将相对路径转换为绝对路径
res.sendFile(path.resolve('./index.html'))
})
app.get('/list', (req, res) => {
res.end(JSON.stringify(list))
})
app.post('/send', (req, res) => {
list.push(req.body.message)
res.end(JSON.stringify(list))
})
app.post('/clear', (req, res) => {
list.length = 0
res.end(JSON.stringify(list))
})
app.listen(3000);
最后运行结果如下图
socketio 实现
说一下 socketio 的优势:
- html5 标准
- 支持优雅降级
跟 websocket 相比,减少了兼容问题(其实还是优雅降级,因为 websocket 就是 html5 标准)。
主要的功能都是 socketio 实现的,例子很好写,看一下 socketio 的官网就能弄明白。所以代码就不贴了,地址在上面
3. 实现一个爬虫
很简单的一个爬虫,就是发送 request 请求获取对应网页,然后解析其中的某一个节点的值。这里面获取的是澎湃新闻的文章标题。
// request 库 也是封装的 http
const originRequest = require("request");
// 服务器端的 jquery
const cheerio = require("cheerio");
// 负责转码
const iconv = require("iconv-lite");
function request(url, callback) {
const options = {
url: url,
encoding: null
};
originRequest(url, options, callback);
}
for (let i = 5227428; i < 5227430; i++) {
// 随便爬取一个澎湃新闻的例子
const url = `https://www.thepaper.cn/newsDetail_forward_${i}`;
request(url, function (err, res, body) {
// 爬虫网页对应的编码使用 document.chartset 就能获取到
const html = iconv.decode(body, "UTF-8");
const $ = cheerio.load(html);
console.log($(".news_title").text());
});
}