Cross-domain is a violation of the browser’s same-origin policy
In short, the most common request tools we use are built on the XMLHttpRequest object, which strictly adheres to the same origin policy, so when cross-domains are required, tools based on it do not work. We need some additional tools to solve this problem. This article focuses on the practice and summary of several known cross-domain solutions
A, the json
Jsonp is not the official cross-domain solution, but it is the first cross-domain solution, and regardless of how you use jSONP cross-domain, == server support for such cross-domain ==.
disadvantages
Since JSONP is a tag that uses script, img, and iframe without origin restrictions, it only supports GET requests
In addition, jSONP error handling mechanism is not perfect
advantages
The advantage of JSONP is that it is compatible with older browsers
Principle 1.
Jsonp uses script, img, iframe tags with no homologous constraints to send requests to the server, taking the returned data as arguments to the specified callback function. The function introduced by the script tag belongs to the window global, so you only need to specify the callback function in another script. In this way, the server data can be retrieved
This is the most basic jSONP cross-domain example
// test.html
<script>
function test(data) {
console.log(data);
return data;
}
</script>
<script
type="text/javascript"
src="Http://127.0.0.1:8090/v1/system/user/getTotal? callback=test"
></script>
Copy the code
The console print is as follows
2. The packaging
For vue to use JSONP, you can use third-party toolkits. Jsonp is recommended, if you like axios style, it will be very handy.
Download a.
cnpm i jsonp --save
Copy the code
B. to introduce
Create a new file for our packaged JSONP tool
// http.js
import jsonp from "jsonp"
Copy the code
According to the JSONP documentation, we know that jSONP requires three parameters
var options = {
parma: "callback".timeout: 5000.prefix: "".name: "callbackFun"
};
// JSONP will concatenate the URL according to this configuration
Url +? callback=callbackFun
// If the url already has arguments: url+&callback=callbackFun
Copy the code
It then returns a Promise-style JSONp function that takes the URL as a parameter, options as an argument, and a callback function
// http.js
export function get(url) {
var options = {
parma: "callback".timeout: 5000.prefix: "".name: "callbackFun"
};
return new Promise((resolve, reject) = > {
jsonp(url, options, (err, res) => {
if (err) {
// Errors can be handled here, such as introducing the toasts of component libraries such as element-UI, etc
// Prompts the user for an error
// You can import different pages according to different HTTP status codes
reject(err);
} else{ resolve(res); }}); }); }Copy the code
Sometimes, we also need to pass parameters to the server. In this case, we need to concatenate the parameter object into the URL. We can also encapsulate this
// Concatenate the Parma argument to the URL
function joinParma(url, parma) {
let str = "";
for (const key in parma) {
str += key + "=" + parma[key] + "&";
}
str = str.substring(0, str.length - 1);
str = url.indexOf("?") = =- 1 ? "?" + str : "&" + str;
return url + str;
}
Copy the code
So the complete code looks like this
import jsonp from "jsonp"
import router from './router'
// Concatenate the Parma argument to the URL
function joinParma(url, parma) {
let str = "";
for (const key in parma) {
str += key + "=" + parma[key] + "&";
}
str = str.substring(0, str.length - 1);
str = url.indexOf("?") = =- 1 ? "?" + str : "&" + str;
return url + str;
}
export function get(url, parma) {
var options = {
parma: "callback".timeout: 5000.prefix: "".name: "callbackFun"
};
// If there are parameters, concatenate parameters, otherwise no processing
if (parma) {
url = joinParma(url, parma);
}
return new Promise((resolve, reject) = > {
jsonp(url, options, (err, res) => {
if (err) {
// Errors can be handled here, such as introducing the toasts of component libraries such as element-UI, etc
// Prompts the user for an error
// You can import different pages according to different HTTP status codes
switch (err.response.status) {
case 500:
router.push({
path: "/ 404"
});
break;
case 401:
router.push({
path: "/ 401"
});
break;
}
reject(err);
} else {
// You can also make hints according to the message handling mechanism agreed with the back end
// You can introduce the toast module of the UI library you are using to prompt the user
if(res.data.code == 200) {console.log("Operation successful")}else if(res.data.code == 300) {console.log("No data available or query failed")}else{
console.log("Operation failed") } resolve(res); }}); }); }Copy the code
Use 3.
By introducing packaged JSONP into our interface file, we can use it as we see fit
// api/getTotal.js
import { get } from '@/http/jsonp'
export function getTotal(data){
return get("http://127.0.0.1:8090/v1/system/user/getTotal",data)
}
Copy the code
You then introduce interface functions into the components to be used
// list.vue
<script>
import { getTotal } from "@/api/getTotal"
export default {
data(){
total: 0.user: {id: 1.name: 'zs'
}
},
created(){
getTotal(this.user)
.then((res) = >{
if (res.data.code == 400) {this.total = res.data.total
}
})
.catch(err= >{
console.log(err)
})
}
}
</script>
Copy the code
Ii. CORS (Cross-domain Resource Sharing)
CORS still uses XMLHttpRequest to send requests, but with additional fields that tell the server to allow cross-domain, == it also requires server support ==
advantages
Multiple requests can be accessed
Perfect processing mechanism
Compliance with HTTP specifications, for complex requests, one more authentication, better security
disadvantages
Internet Explorer 10 or later is not supported
To illustrate, here is the server-side code for the test
const http = require("http");
const server = http
.createServer((req, res) = > {
res.setHeader("Access-Control-Allow-Origin"."*");
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello World\n");
})
.listen(8090);
console.log("Server running at http://127.0.0.1:8090/");
Copy the code
And a client code, which you can put in a pure HTML page and a vUE project if you are interested
let ajax;
if (XMLHttpRequest) {
ajax = new XMLHttpRequest();
} else {
ajax = new ActiveXObject();
}
ajax.onreadystatechange = function(res) {
if (ajax.readyState == 4 && ajax.status == 200) {
console.log(ajax.responseText); }}; ajax.open("GET"."http://127.0.0.1:8090".true);
ajax.send();
Copy the code
Principle 1.
Cross-domain resource sharing (CORS) is a cross-domain solution officially recommended by HTTP. It is a mechanism that uses extra HTTP headers to tell browsers that Web applications running on one Origin (domain) are allowed to access specified resources from different source servers. This is the standard definition of MDN for CORS. Simply add an origin field to the server when sending the request. The server determines whether it is acceptable and returns an Access-Control-Allow-Origin field
For example, in the example above, whether it’s a VUE project or a separate HTML page, you can see the “Hello World” returned by the server on the console,
And the fields are as follows
// Request header // Vue project"Origin": "http://localhost:8080"/ / HTML page"Origin": null
// response header
"Access-Control-Allow-Origin":"*"
Copy the code
When the field value of the server is changed to http://localhost:8080, only the vUE project of http://localhost:8080 can cross domains, and the HTML page displays an error
Access to XMLHttpRequest at 'http://127.0.0.1:8090/' from origin 'null'
has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header
has a value 'http://localhost:8080' that is not equal to the supplied origin.
Copy the code
Note that http://localhost:8080 and http://127.0.0.1:8080 are not the same for the server
CORS classifies requests into “simple requests” and “non-simple requests” based on whether or not they use fields and methods supported by default. The browser treats them differently depending on the type. The difference is that simple requests do not invoke validation, while non-simple requests invoke validation
For simple (CORS default) requests, it has no more than three methods and no more than the following header fields
(1) Request method: HEAD GET POST (2) HTTP header: Accept accept-language content-language last-event-id content-type: Application/X-www-form-urlencoded, multipart/form-data, text/plainCopy the code
== Note == Simple request:
- Cookies cannot be received or sent
- Custom headers cannot be set using setRequestHeader()
- Calling getAllResponseHeader() yields an empty string
But are we not surrounding a Request but a non-simple Request but a browser called a Preflrequest transparent server verification mechanism, using the OPTIONS method and the server verification, then the browser formally requested a server.
SetRequestHeader and getAllResponseHeader are not supported by CORS by default. For non-simple requests, you can use the OPTIONS method to override this default
// client ajax.open("GET"."http://127.0.0.1:8090".true);
ajax.setRequestHeader("X-PINGOTHER"."pingpong");
ajax.setRequestHeader("Content-Type"."application/xml"); ajax.send(); // The server needs to set the field res.setheader ("Access-Control-Allow-Headers"."Access-Control-Allow-Headers, Origin,Accept,X-PINGOTHER,X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"
);
Copy the code
In this case, the network will have an additional request to verify
If the server does not set this field permission, it will report an error at the console
Request header field x-pingother is not allowed by Access-Control-Allow-Headers in preflight response.
Copy the code
If you need to send or receive cookies, you can set the withCredentials attribute to True, and the server needs to cooperate to accept requests with cookies. The response header returns the following fields and values
/ / the client
ajax.open("GET"."http://127.0.0.1:8090".true);
ajax.withCredentials = true;
ajax.send();
/ / the server
res.setHeader("Access-Control-Allow-Credentials".true);
// The following fields are added to the response packet
"Access-Control-Allow-Credentials": true
Copy the code
If the server sets the Credentials to false, the onError program is raised with status 0 and responseText as an empty string
res.setHeader("Access-Control-Allow-Credentials".false);
Copy the code
2. The packaging
Use AXIos in the VUE project to encapsulate the business
Download a.
cnpm i axios -s
Copy the code
B. to introduce
Create HTTP /request.js and store our wrapped AXIos file
// http/request.js
/ / introduction
import axios from "axios";
// Create an instance
const server = axios.create({
// Set the address we visit to baseURL
baseURL: "http://127.0.0.1:8090".// Set the timeout period
timeout: 5000Headers: {"Content-Type":"text/plain"."Access-Control-Allow-Credentials": true}});Copy the code
In a project, there is often a need for uniform processing of requests, for example, putting cookies on each request, responding to all responses, importing different pages based on different error states, and using axios’s interceptor to achieve the desired effect
// Set interceptor
// Request interceptor
server.interceptors.request.use(
config= > {
// Each request is authenticated with a token to the server
config.headers.token = localStorage.getItem("token");
return config;
},
error => {
console.log(error);
Promise.reject(error); });// Response interceptor
server.interceptors.response.use(
response= > {
// You can also make hints according to the message handling mechanism agreed with the back end
// You can introduce the toast module of the UI library you are using to prompt the user
if(response.data.code == 200) {console.log("Operation successful")}else if(response.data.code == 300) {console.log("No data available or query failed")}else{
console.log("Operation failed")}return response;
},
error => {
switch (
error.response.status
) {
case 500:
router.push({
path: "/ 404"
});
break;
case 401:
router.push({
path: "/ 401"
});
break; }});export default server;
Copy the code
Use 3.
Add the wrapped AXIos to our interface file and you can use it
// api/getTotal.js
import axios from '@/http/request'
export function getTotal(){
return axios({
url: "/system/user/getTotal".method: 'get'})}Copy the code
You then introduce interface functions into the components to be used
// list.vue
<script>
import { getTotal } from "@/api/getTotal"
export default {
data(){
total: 0,
},
created(){
getTotal()
.then((res) = >{
if (res.data.code == 400) {this.total = res.data.total
}
})
.catch(err= >{
console.log(err)
})
}
}
</script>
Copy the code
Iii. Proxy (Server-side Proxy)
In the VUE project, the Webpack option devServer has a proxy attribute, which is typically used to send all requests with a specified string to the target target server. Looking at the documentation, devServer invokes the HTTP-proxy-Middleware plug-in to forward the request to the target server.
Here is an example from the official website
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
app.use(
'/api',
proxy({ target: 'http://www.example.org'.changeOrigin: true})); app.listen(3000);
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
Copy the code
To illustrate how this works, I created two Node services myself, one emulated client and one emulated target server
Principle 1.
On the client side, create a service using HTTP and return the displayed page
const http = require("http");
const fs = require("fs");
const server = http.createServer();
server.listen(8090);
server.on("request", (req, res) => {
res.writeHead(200, { "Content-Type": "text/html" });
if (req.url == "/") {
fs.readFile("./index.html", (err, data) => {
if (err) {
console.log(err);
} else{ res.end(data); }}); }});console.log("Server running at http://127.0.0.1:8090/");
Copy the code
In addition, in the page, you can define or reference ajax already written, and send a request to the client server. When the client receives the request, it forwards it to the target server
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>hello world</title>
</head>
<body>
<button onclick="openUrl()">Test the connection</button>
<script>
let ajax;
if (XMLHttpRequest) {
ajax = new XMLHttpRequest();
} else {
ajax = new ActiveXObject();
}
function openUrl() {
ajax.open("get"."/api/index".false);
ajax.send();
}
</script>
</body>
</html>
Copy the code
// Client server
if (req.url == "/") {
fs.readFile("./index.html", (err, data) => {
if (err) {
console.log(err);
} else{ res.end(data); }}); }else if (req.url.search("/api") != - 1) {
http.get("http://127.0.0.1:8091" + req.url, response => {
let data = "";
response.on("data", chunk => {
data += chunk;
});
response.on("end", () => {
res.end(data);
});
});
console.log(req.url, "Come in.");
}
Copy the code
The target server is responsible for receiving different urls and responding to them separately
// Target server
const http = require("http");
const server = http.createServer();
server.listen(8091);
server.on("request", (req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
if (req.url == "/api/index") {
// Here we send a JSON object to the client server
let data = {
id: 1.name: "cjr"
};
res.end(JSON.stringify(data)); }});console.log("Server running at http://127.0.0.1:8091/");
Copy the code
The browser console can now receive the JSON data sent by the target server
2. Encapsulation and use
In addition to setting the base path, we need to specify the cross-domain destination server address
devServer:{
'/api':{
target: "http://127.0.0.1:8091"// Remember to add HTTP changeOrigin:true// set this to allow cross-domain}}Copy the code
Again using AXIos, encapsulating and using the same as Cros, except baseURL is different and only the complete code is presented here
/ / introduction
import axios from "axios";
// Create an instance
const server = axios.create({
// Set the address we visit to baseURL
baseURL: "/v1".// Set the timeout period
timeout: 5000
});
// Set interceptor
// Request interceptor
server.interceptors.request.use(
config= > {
// Each request is authenticated with a token to the server
config.headers.token = localStorage.getItem("token");
return config;
},
error => {
console.log(error);
Promise.reject(error); });// Response interceptor
server.interceptors.response.use(
response= > {
// You can also make hints according to the message handling mechanism agreed with the back end
// You can introduce the toast module of the UI library you are using to prompt the user
if(response.data.code == 200) {console.log("Operation successful")}else if(response.data.code == 300) {console.log("No data available or query failed")}else{
console.log("Operation failed")}return response;
},
error => {
switch (
error.response.status
) {
case 500:
router.push({
path: "/ 404"
});
break;
case 401:
router.push({
path: "/ 401"
});
break; }});export default server;
Copy the code
More plans
In addition to the cross-domain approach mentioned earlier, you can also use a Nginx reverse proxy, which is very similar to a proxy in that it accesses the middle tier and the middle tier accesses the target server
There are also clever ways to cross domains, such as when you use iframe tags in your pages, hash location.hash, and cross domains and pass data. You can also use window.name or H5’s postMessage interface
These cross-domain solutions are unpopular in practice, and I have not practiced them, so it is briefly mentioned here
If there are any mistakes in the article, please point them out and I will correct them in time