cors 跨域问题

最近遇到一个跨域问题,弄了一段时间,所以记录下

简单的GET 请求之类的,走jsonp就好了,但是这次涉及到POST,所以就弄了CORS。参看 这篇Cors跨域(一):深入理解跨域请求概念及其根因,大致了解了其实本质上就是要在返回的header时候加上几个,类似Access-Control-Allow-Origin 等等。

于是尝试在返回的header里加上了几个 header。但是奇怪的事情发生了。get请求跨域正常,同事写的POST也正常,偏偏是我写的 post 接口异常。

代码如下

 @RequestMapping(value = "home/applySubmission.do", method = {RequestMethod.POST, RequestMethod.OPTIONS})
    @ResponseBody
    public Map<String, Object> applySubmission(HttpServletRequest request, @RequestBody Map<String, Object> param) {
	//业务逻辑    
}

后来才发现是因为 我发的跨域不是简单请求 ,简单请求定义
何为简单请求

Cors规范定义简单请求的原则是:请求不是以更新(添加、修改和删除)资源为目的,服务端对请求的处理不会导致自身维护资源的改变。对于简单跨域资源请求来说,浏览器将两个步骤(取得授权和获取资源)合二为一,由于不涉及到资源的改变,所以不会带来任何副作用。

对于一个请求,必须同时符合如下要求才被划为简单请求:

Http Method只能为其一:
    GET
    POST
    HEAD
请求头只能在如下范围:
    Accept
    Accept-Language
    Content-Language
    Content-Type,其中它的值必须如下其一:
        application/x-www-form-urlencoded
        multipart/form-data
        text/plain

除此之外的请求都为非简单请求(也可称为复杂请求)。非简单请求可能对服务端资源改变,因此Cors规定浏览器在发出此类请求之前必须有一个“预检(Preflight)”机制,这也就是我们熟悉的OPTIONS请求。


而我同事恰好 是 application/x-www-form-urlencoded ,我是json 那肯定是复杂的请求,需要preflight机制 ,提前发一个options 请求。搜了一圈 ,网上的套路大部分都是springboot,一看我们的项目 是spring 3.x 。好像没有现成的轮子。后来 大概看了两种思路

  1. 自己写一个filter ,返回体加上各种 cors x需要的头(觉得这样更好,简单参考 博客这里)
  2. 自己写一个拦截器(我采用的)

如果走拦截器,那必然会要走 Dispatchservlet 中
首先必须要配置这个 ,要让框架处理 option 请求

<servlet>
		<servlet-name>xxx</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:conf/spring/spring-servlet.xml</param-value>
		</init-param>
		<init-param>
			<param-name>dispatchOptionsRequest</param-name>
			<param-value>true</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

否则在FrameworkServlet 由于 dispatchOptionsRequest 默认false ,好像走了父类方法,就不会走你写的 拦截器

/**
	 * Delegate OPTIONS requests to {@link #processRequest}, if desired.
	 * 

Applies HttpServlet's standard OPTIONS processing otherwise, * and also if there is still no 'Allow' header set after dispatching. * @see #doService */ @Override protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (this.dispatchOptionsRequest) { processRequest(request, response); if (response.containsHeader("Allow")) { // Proper OPTIONS response coming from a handler - we're done. return; } } super.doOptions(request, response); }

这里再补充一点
方法上的 method = {RequestMethod.POST, RequestMethod.OPTIONS},一定要加上 RequestMethod.OPTIONS
,否则会报handler 找不到 方法 ,都不会再到 拦截链那里!!
最后拦截器写法

public class CorsFilter implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        if(request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            response.setStatus(HttpStatus.OK.value());
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
            response.setHeader("Access-Control-Allow-Headers", "Content-Type, Content-Length, Authorization, Accept, X-Requested-With");
            response.setHeader("Access-Control-Max-Age", "86400");
            response.setHeader("Allow", "true");
            return false;
        }
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
    }

}

你可能感兴趣的:(开发工具,spring)