梗概
- CORS会将请求分成两种类型进行处理
- 简单请求
- 复杂请求
简单请求的CORS方式
- 后端需要给响应头中添加指定字段
Access-Control-Allow-Origin: 允许的URL- 可以使用通配符
- 如
Access-Control-Allow-Origin:*
- 如
- 可以使用通配符
实例
Java
那在java服务端给响应头设置 Access-Control-Allow-Origin 可以这么做: 1、添加一个过滤器
public class CrossDomainFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse)servletResponse;
resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000");
filterChain.doFilter(servletRequest,servletResponse);
}
public void destroy() {}
}2、然后在web.xml文件中添加过滤器配置:
<filter>
<filter-name>crossDomainFilter</filter-name>
<filter-class>com.example.cors.filter.CrossDomainFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>crossDomainFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>复杂请求的CORS方式
- 对于POST请求设置响应头Content-Type为某些值、自定义请求头等情况,浏览器会先以OPTIONS方法发送一个预检请求,并设置相应的请求头。
- 后端需要给[use::预检]请求响应对应的响应头,这样才算预检通过
- 预检通过之后,前后端就可以直接进行跨域通信
- 而不用设置其他响应头和请求头
实例
比如 Client 发送如下请求:
new Request().send('http://localhost:8080/server/options',{
method: 'POST',
header: {
'Content-Type': 'application/json' //告诉服务器实际发送的数据类型
},
success: function(data){
document.write(data)
}
});Server 端处理请求的 controller:
@Controller
@RequestMapping("/server")
public class CorsController {
@RequestMapping(value="/options", method= RequestMethod.POST)
@ResponseBody
public String options(HttpServletRequest request) throws Exception{
return "SUCCESS";
}
}浏览器会以 OPTIONS 方法发出一个预检请求,浏览器会在请求头中加入:
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST这个预检请求的作用在这里就是告诉服务器:我会在后面请求的请求头中以 POST 方法发送数据类型是application/json 的请求,询问服务器是否允许。
Server端过滤器中添加Access-Control-Allow-Headers:
public class CrossDomainFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse)servletResponse;
resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000");
resp.setHeader("Access-Control-Allow-Headers", "Content-Type");
filterChain.doFilter(servletRequest,servletResponse);
}
public void destroy() {}
}再来看请求的具体信息,第一次以 OPTIONS 方法发送预检请求,浏览器设置请求头:
Access-Control-Request-Headers:content-type //请求中加入的请求头
Access-Control-Request-Method:POST //跨域请求的方法服务端设置响应头:
Access-Control-Allow-Headers:Content-Type //允许的header
Access-Control-Allow-Origin:http://localhost:8000 //允许跨域的源可以看到 POST 请求成功了,第二次请求头中没有设置 Access-Control-Request-Headers 和 Access-Control-Request-Method。 但是这里有个问题,需要预检请求时,浏览器会发出两次请求,一次 OPTIONS,一次 POST。两次都返回了数据。这样服务端如果逻辑复杂一些,比如去数据库查找数据,从 web 层、 service 到数据库这段逻辑就会走两遍,浏览器会两次拿到相同的数据,所以服务端的 filter 可以改一下,如果是 OPTIONS 请求,在设置完跨域请求响应头后就不走后面的逻辑直接返回。
public class CrossDomainFilter implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse)servletResponse;
resp.setHeader("Access-Control-Allow-Origin", "http://localhost:8000");
resp.setHeader("Access-Control-Allow-Headers", "Content-Type");
//OPTION请求就直接返回
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getMethod().equals("OPTIONS")) {
resp.setStatus(200);
resp.flushBuffer();
}else {
filterChain.doFilter(servletRequest,servletResponse);
}
}
public void destroy() {}
}