Vue + Axios 需要Api能够跨域调用,网上也是很多跨域的解决方法,比如重写 WebMvcConfigurationSupport 的addCrosMappings 方法,如:
public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET","POST","PUT","DELETE","OPTIONS") .allowedHeaders("X-Access-Token"); log.debug("允许跨域配置成功!"); }
这种方法正常能够跨域,但是遇到进行 Token 认证被拦截器或者过滤器处理成401并返回后,跨域也就失败,axios 会读不到error.response,而无法进行401处理。@CrossOrigin更是不能解决,况且每个 Controller加也不方便。
我的解决方法简单介绍如下:
1、定义拦截器CrossInterceptor,并且能够通过配置修改允许跨域域名
import lombok.extern.log4j.Log4j2; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import java.util.List; @Log4j2 @Configuration @ConfigurationProperties(prefix = "spring.cross") public class CrossInterceptor implements HandlerInterceptor { private static List<String> allowHosts; //配置允许跨域的域名 public void setAllow(String allow){ if(allow != null){ allowHosts = Arrays.asList(allow.split(",")); } } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String origin = request.getHeader(HttpHeaders.ORIGIN); if (origin != null) { if(allowHosts.contains(origin)) { response.setHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD"); response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Access-Token"); response.setHeader("Access-Control-Max-Age", "3600"); } else { log.warn("跨域失败:" + origin); } } return true; } }
2、WebMvcConfigurationSupport中注册拦截器,注意跨域拦截器必须要Token认证、权限认证等拦截器之前注册
import lombok.extern.log4j.Log4j2; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; @Log4j2 @Configuration class WebMvcConfiguration extends WebMvcConfigurationSupport { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CrossInterceptor()).addPathPatterns("/**"); log.debug("跨域拦截器注册成功!");
registry.addInterceptor(new OptionsInterceptor()).addPathPatterns("/**"); log.debug("Options请求拦截器注册成功!");
// 不在这里实现跨域,这里实现跨域会导致拦截器return false的情况下跨域失败,改为拦截器实现 /*@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET","POST","PUT","DELETE","OPTIONS") .allowedHeaders("X-Access-Token"); log.debug("允许跨域配置成功!"); } */ }
3、配置yml
spring: cross: allow: http://localhost:8080,http://localhost:8081
4、Axios 处理401等返回结果
function beforThen (instance) { instance.interceptors.response.use(function (response) { let { data } = response; return data; }, function (error) { if (error.response && error.response.status === 401) { window.location.href = '/#/login'; } else { Message.error(error.message); return Promise.reject(error.message); } }); }
5、Axios请求时,偶尔会遇到一个 api请求两次的情况,其实第一次是 Options 请求,我们也可以用拦截器同意处理 Options请求
import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class OptionsInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if(request.getMethod().equals("OPTIONS")){ response.setStatus(HttpServletResponse.SC_OK); return false; } return true; } }该拦截器也是要注册在跨域之后,保证 Options请求能够跨域成功