一、浏览器同源策略引起的接⼝调⽤问题
1. 跨域常用解决方案
- JSONP(JSON with Padding),前端+后端⽅案,绕过跨域
前端构造script标签请求指定url,由script发出GET请求不受同源策略限制。服务器返回一个函数执行语句,函数的参数为服务器返回的json数据。函数在前端执行后即可返回数据。 - 代理服务器
服务器访问服务器没有跨域问题,所以可以利用中间的代理浏览器向目标浏览器发请求。代理跨域的主要方式是利用服务器请求服务器的方式避过跨域问题来实现的。大概的流程:浏览器===>代理服务器===>目标服务器。
扩展:正向代理即是客户端代理,代理客户端,服务端不知道实际发起请求的客户端;反向代理即是服务端代理,代理服务端,客户端不知道实际提供服务的服务端。正向代理场景中,客户端需要配置代理服务器的ip地址,而反向代理服务器使用的是我们对外服务的域名,用户访问我们域名的时候,根本不知道真正的服务器是什么。 - CORS(Cross Origin Resource Share) - 跨域资源共享,后端⽅案,解决跨域。
CORS 请求分成两类:简单请求)和非简单请求。原理:cors是w3c规范,真正意义上解决跨域问题。它需要服务器对请求进⾏检查并对响应头做相应处理, 从⽽允许跨域请求。- 简单请求
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。 如果origin在允许范围内,服务器响应信息如下:
Access-Control-Allow-Origin: http://api.bob.com //必需,表示哪个域可以请求 Access-Control-Allow-Credentials: true //允许客户端可以发送 Cookie Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8- 非简单请求
非简单请求是那种对服务器提出特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。浏览器发现,这是一个非简单请求,就自动发出一个“预检”请求,要求服务器确认可以这样请求。
预检请求的回应如下:OPTIONS /cors HTTP/1.1 Origin: http://api.bob.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Methods: GET, POST, PUT 必需,表明服务器支持的所有跨域请求的方法。 Access-Control-Allow-Headers: X-Custom-Header //如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。表明服务器支持的所有头信息字段。 Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain - 简单请求
2. 具体实现
- CORS-简单请求
const http = require('http')
http.createServer(function(request,response){
const {url,method} = request;
if(url=='/user' && method=='GET'){
response.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000')
//对响应头设置允许该域名对应的客户端对服务器发出请求
const data = JSON.stringify([{"name":"jucy","olds":"18"}]);
response.setHeader('Content-Type',"application");
response.end(data);
}
}).listen(3000)
- CORS-非简单请求
跨域请求cookie和设置headers问题。判断method==options,进行预检。预检通过后进入get方法。
//index.html
<script>
(async () => {
axios.defaults.baseURL = 'http://localhost:3000'
axios.defaults.withCredentials = true
//指定请求应该发送凭证
const res = await axios.get("/user", {headers:{'X-Token':'jilei'}})
document.writeln(`Response : ${JSON.stringify(res.data)}`)
})()
</script>
const http = require('http')
http.createServer(function(request,response){
const {url,method} = request;
if(url=='/user' && method=='GET'){
const data = JSON.stringify([{"name":"jucy","olds":"18"}]);
response.setHeader('Content-Type',"application/json");
response.setHeader('Set-Cookie', 'cookie1=jucious;')
//服务器接收带凭据的请求,会用下面的HTTP头部来响应
response.setHeader('Access-Control-Allow-Credentials','true');
response.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
response.end(data);
}else if(url=='/user' && method=='OPTIONS'){
response.writeHead(200, {
"Access-Control-Allow-Origin": "http://localhost:8000",
"Access-Control-Allow-Headers": "X-Token,Content-Type",
"Access-Control-Allow-Credentials": "true",
});
response.end();
}
}).listen(3000)
- http-proxy-middleware用于后台将请求转发给其它服务器,用法如下:
var proxy = require('http-proxy-middleware');
var apiProxy = proxy('/api', {target: 'http://www.example.org'});
// \____/ \_____________________________/
// | |
// 哪些请求要被目标主机代理 目标主机
var express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware')
const app = express()
app.use(express.static(__dirname + '/'))
app.use('/api', createProxyMiddleware({ target: 'http://localhost:3000', changeOrigin: true}));
module.exports = app
//proxy('http://www.example.org:3000/api');
等价于 proxy('/api', {target: 'http://www.example.org:3000'});
//假如当前域名为localhost:4000,发送请求url为localhost:4000/user,而真实的接口地址为localhost:3000/user,就可以使用proxy转发。
二、其他网络编程
1.body-parser
利用原生解析理解: Node.js 原生HTTP模块中,是将用户请求数据封装到了用于请求对象req中,该对象是一个IncomingMessage,该对象同时也是一个可读流对象。
if (method === "POST" && url === "/api/save") {
let reqData = [];
let size = 0;
req.on('data', data => {
console.log('>>>req on', data);
reqData.push(data);
size += data.length;
});
req.on('end', function () {
const data = Buffer.concat(reqData, size);
console.log('data:', size, data.toString())
res.end(`formdata:${data.toString()}`)
});
}
2.实现⼀个即时通讯IM
- Socket.IO 由两部分组成:
一个服务端用于集成 (或挂载) 到 Node.JS HTTP 服务器:socket.io; 一个加载到浏览器中的客户端: socket.io-client - 具体代码实现
//客户端
$(function() {
var socket = io();
$('form').submit(function(e){
e.preventDefault();
socket.emit('message',$('input').val())
//发送消息
})
socket.on('message',function(msg){
$('ul').append(`<li>${msg}<li>`)
})
//接收消息
})
//服务器
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http)
app.get('/',function(req,res){
res.sendFile(__dirname + '/index.html')
})
io.on('connection',function(socket){
socket.on('message',function(msg){
//接收到客户端传来的消息后广播出去
socket.emit('message',msg)
})
})
http.listen(3030,function(){
console.log('listen:3030')
})