高级前端软件工程师知识整理之跨域篇

1. ajax如何处理跨域?

ajax使用jsonp请求实现跨域,关于这个网上已经很多资料了,基本用法这里就不介绍了。其原理可以参考我的另一篇文章《18~19年大厂高级前端面招汇总之基础篇(一)》中的【3. jsonp实现的原理是什么?】,但如果继续深究,很少资料能介绍清楚其参数jsonp和jsonpCallback的区别,以及jsonpCallback的回调函数与success的回调函数的区别,这里用实例再补充分析一下,请看例子:

$(document).ready(function() {
	var data = {};
	var jsonData = JSON.stringify(beanObject);
	$.ajax({
		type: 'GET',
		url: 'http://localhost:8080/ninVoiceService_V2/NewTTSJP',
		dataType: 'jsonp',
		data: {...data},
		jsonp: 'jsonpcallback',
		jsonpCallback: 'todo',
		error: function(XmlHttpRequest, textStatus, errorThrown) {
			console.log("error");
		},
		success: function(resp) {
			console.log('success');
			console.log(resp);
		}
	});
});

function todo(resp){
	console.log('todo'); 
	console.log(resp);
}

// 执行结果
// todo
// {...resp}
// success
// {...resp}

关于参数 jsonp: 'jsonpcallback',默认值为'callback',这个参数主要是服务端用到的,前端只负责定义名称。主要是为了告诉服务器这个是我要的回调函数名称,服务端响应代码如下:

String callback = request.getParameter("jsonpcallback");
// 处理过程...
PrintWriter out = response.getWriter();
out.print(callback + "(" + respObj.toString() + ");");
out.flush();
out.close();

如果前端不设置jsonp值,即使用默认值,则服务端修改如下即可:

String callback = request.getParameter("callback");

关于参数 jsonpCallback: 'todo', 了解jsonp原理就很清楚这个参数的用意,这个参数只跟前端有关,跟服务端边都沾不上,主要用于接收服务端返回出数据的回调函数,它跟success的用途是一样的。这里就有人疑问了,刚告诉服务器我要的回调函数名称不是在参数jsonp: xxx 中设置了吗,这个又是怎么回事?原因很简单,ajax实现了允许前端和后端回调函数名称不同的功能,步骤大概如下:

1)前端告诉服务端,我要发送jsonp请求啦,我的回调函数是 jsonp: 'jsonpcallback'。这里注意,实际上传递给服务端的是url?jsonpcallback= ???? ,即回调函数的实际名称是一串随机字符串而不是jsonpcallback。

2)服务端说,我知道了,我的响应结果是  jsonpcallback({...data}),实际上是 ????({...data})

3)ajax说,我收到jsonpcallback({...data})响应了,你想要执行的实际回调函数名称??? 我这里没有,我给你转成其它函数吧

4)ajax检查自己发现有一个 jsonpCallback: 'todo',那我就转成todo({...data})

5)ajax把回调函数转成todo({...data})执行后,按照自身设定,请求成功还要将回调函数再转换为success函数执行。

(小贴士)正常情况下,这个参数jsonpCallback是无需设置的,使用success就好。

2. 表单可以跨域吗?

表单提交是可以跨域的。在前后端还没分离的时代,就经常使用表单提交验证用户登录,验证通过后由服务端重定向实现页面跳转。为什么可以跨域提交?因为原页面用 form 提交到另一个域名之后,原页面的脚本是无法获取新页面中的任何内容和数据的,所以浏览器认为这是安全的。这是个历史问题,现在基本不会使用<form>标签来请求数据。

3. CORS是什么?CORS如何设置?

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求如,站点 http://domain-a.com 的某 HTML 页面通过 <img> 的 src 请求 http://domain-b.com/image.jpg,像这类通过src的请求跨域是允许的。但出于安全原因,浏览器限制了从脚本内发起的跨源HTTP请求, 例如,XMLHttpRequest和Fetch API,它们必须遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。

这里推荐一篇文章《跨域资源共享 CORS 详解》

要通过CORS机制实现跨域,就需要在服务端header中配置跨域,主要配置项包括:

  • Access-Control-Allow-Origin 必须字段,表示访问来源的域名或ip地址+端口号,也可以设置为*表示所有域都可以通过。
  • Access-Control-Allow-Methods 必须字段,表示允许跨域的请求方法。
  • Access-Control-Allow-Headers 可选字段,表示允许跨域请求中包含在请求头的字段。
  • Access-Control-Allow-Credentials 可选字段,当客户端也把Credentials设为true时,表示请求头中会带上cookie。

服务端使用的语言不同,则配置的方法不同,在node服务端express框架中,可使用:

var express=require('express');
var app=express();
var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Token');
    res.header('Access-Control-Allow-Credentials','true');
    res.header("Content-Type", "application/json;charset=utf-8"); // 如果返回json数据格式,则加上这句
    next();
};
app.use(allowCrossDomain);

Java服务端的配置,可使用:

response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept, Token");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setContentType("application/json;charset=utf-8"); // 如果返回json数据格式,则加上这句
// response.setHeader("Access-Control-Max-Age", "3628800"); // 表示特定时间内不需要再发送预检验请求,可以缓存该结果,一般默认,不做设置。

客户端ajax请求:

var data = {}; // 参数
$.ajax({
	url: url,
	type: 'POST', // 请求方法:GET,PUT,POST,DELETE,PATCH,OPTIONS
	data: { ...data},
	dataType: 'json',
	crossDomain: true,
	xhrFields: {
		withCredentials: true // 请求头自动带上cookie
	},
	headers: {
		'Content-Type': 'application/json', // 发送给服务端的数据格式
		'Accept': 'application/json' // 接收服务端返回的数据格式
		'Token': 'TokenInfo' // Token验证
	},
	success: function(response) {},
	error: function(XmlHttpRequest, textStatus, errorThrown) {}
});

客户端axios请求:

import axios from 'axios'

axios.defaults.crossDomain = true;
axios.defaults.withCredentials = true;
axios.interceptors.request.use(
	function(config) {
            let headertoken = 'TokenInfo';
            config.headers['Content-Type'] = 'application/json';
            config.headers['Accept'] = 'application/json';
            config.headers['TOKEN'] = `${headertoken}`;
            return config;
	},
	function(error) {
		return Promise.reject(error);
	}
);

var body = {};
var url = '';
axios({
	method: 'POST', // 请求方法:GET,PUT,POST,DELETE,PATCH,OPTIONS
	url,
	data: { ...body} // 请求方法为GET时,属性改为 params: { ...query}
}).then((response) => {
	console.log(response);
}, (err) => {
	console.log(err);
});

客户端Fetch请求,针对CORS跨域机制,fetch有一个专门属性配置:mode,它有三个枚举的配置项:

  • same-origin 该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域。
  • cors 当服务端有配置了CORS,则使用该配置项。另外,如果服务端恰巧同域而且没有设置CORE,依然允许通信。
  • no-cors 服务端没有配置CORS,但使用该配置项依然可以向服务端发送请求,只不过获取不到任何响应数据。

这里还有一个值得注意的点,使用mode: 'cors'配置时,支持三种content-type,但恰恰不支持 application/json:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

另外,针对服务端的"Access-Control-Allow-Credentials" 是否支持cookie传输,在Fetch中也有专门的配置项credentials,它有三个值:

  • include 表示请求时带上cookie
  • same-origin 表示只有在同源请求时带上cookie
  • omit 不带cookie

较常用的是multipart/form-data格式,请求代码如下:

let url = '';
let data = JSON.stringify({
	name: 'admin'
});
const body = new FormData();
body.append('data', data);
fetch(url, {
		method: 'POST',
		body,
		headers: {
			'Content-Type': 'multipart/form-data',
			'Accept': 'application/json',
			'Token': 'TokenInfo'
		},
		mode: 'cors',
                credentials: 'include',
	})
	.then((resp) => resp.json())
	.then((json) => {
		console.log('请求成功:', JSON.stringify(json));
	})
	.catch((error) => {
		console.log(error);
	});

如果使用application/x-www-form-urlencoded格式,请求代码如下:

let url = '';
fetch(url, {
	method: 'POST',
	body: JSON.stringify({name: 'admin'});
	headers: {
		'Content-Type': 'application/x-www-form-urlencoded',
		'Accept': 'application/json',
		'Token': 'TokenInfo'
	},
	mode: 'cors',
	credentials: 'include',
})
.then((resp) => resp.json())
.then((json) => {
	console.log('请求成功:', JSON.stringify(json));
})
.catch((error) => {
	console.log(error);
});

有关Fetch的使用手册,请参考 Fetch API 。

4. 介绍下浏览器跨域?怎么去解决跨域问题?

跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com 页面去请求 www.google.com 的资源。但是一般情况下不能这么做,它是由浏览器的同源策略造成的,跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域。

解决跨域的方法有多少种:

  • CORS跨域资源共享。
  • JSONP
  • Iframe,通过postMessage和getMessage通信
  • 代理服务

具体参考:《ajax跨域,这应该是最全的解决方案了》

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值