前言
项目中定义了跨域过滤器,有些接口请求时出现错误,浏览器上却显示了跨域错误。
一、现象
Access to XMLHttpRequest at 'http://192.168.100.73:7901/demo/ftpFile/upload?project=1`1&projectVersion=11&relatedSoftware=CppUnit%E8%A2%AB%E6%B5%8B%E4%BB%B6&unit=11&type=document' from origin 'http://192.168.100.73:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
POST http:
二、跨域过滤器
public class CustomCorsFilter extends CorsFilter {
private CorsProps corsProps;
public CustomCrosFilter(CorsConfigurationSource configSource, CorsProps corsProps) {
super(configSource);
this.corsProps = corsProps;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
response.setHeader("Access-Control-Allow-Origin", corsProps.getOrigin());
response.setHeader("Access-Control-Allow-Credentials", corsProps.getCredentials());
response.setHeader("Access-Control-Allow-Methods", corsProps.getMethods());
response.setHeader("Access-Control-Allow-Headers", corsProps.getHeaders());
filterChain.doFilter(request, response);
}
三、分析
1、通过调试,请求经过跨域过滤器时,响应头中设置了跨域的信息;
2、请求出现异常时,异常抛给了web容器,这里使用的容器是Undertow,在这里响应头被清理掉了;
response.reset();
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
exchange.getResponseHeaders().clear();
String location = servletContext.getDeployment().getErrorPages().getErrorLocation(t);
3、在找到对应错误页面或默认错误页面后,没有再次通过跨域过滤器设置跨域信息,导致形响应结果中没有带跨域信息,引起浏览器报跨域的错。
四、解决方式
1、在错误页面中设置跨域信息
每个方法都调用一下设置跨域信息的方法
@RestController
public class ErrorPageController {
@Autowired
private CorsProps corsProps;
@RequestMapping(value = "/400", produces = {MediaType.APPLICATION_JSON_VALUE})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity to400() {
setCors();
return new ResponseEntity(400, "请求有误");
}
@RequestMapping(value = "/404", produces = {MediaType.APPLICATION_JSON_VALUE})
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity to404() {
setCors();
return new ResponseEntity(404, "找不到资源");
}
@RequestMapping(value = "/408", produces = {MediaType.APPLICATION_JSON_VALUE})
@ResponseStatus(HttpStatus.REQUEST_TIMEOUT)
public ResponseEntity to408() {
setCors();
return new ResponseEntity(408, "请求超时");
}
@RequestMapping(value = "/500", produces = {MediaType.APPLICATION_JSON_VALUE})
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity to500() {
setCors();
return new ResponseEntity(500, "服务器错误");
}
public void setCors() {
HttpServletResponse response = SpringUtils.getResponse();
String origin = SpringUtils.getRequest().getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", corsProps.getCredentials());
response.setHeader("Access-Control-Allow-Methods", corsProps.getMethods());
response.setHeader("Access-Control-Allow-Headers", corsProps.getHeaders());
}
}
这样错误信息就变成了正常的信息:
POST http:
Uncaught (in promise) {status: 400, message: "请求有误", desc: null, stackTrace: null, value: null, …}
2、在切面中设置跨域信息
统一设置跨域信息
@Aspect
@Component
public class CorsAspect {
@Autowired
private CrosProps corsProps;
@Pointcut("execution(* com.iscas.biz.config..error..*.*(..))")
public void errorMethodPointcut() {
}
@Around(value = "errorMethodPointcut()")
public Object around(final ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletResponse response = SpringUtils.getResponse();
String origin = SpringUtils.getRequest().getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", corsProps.getCredentials());
response.setHeader("Access-Control-Allow-Methods", corsProps.getMethods());
response.setHeader("Access-Control-Allow-Headers", corsProps.getHeaders());
Object result = joinPoint.proceed();
return result;
}
}
3、在请求错误页面ErrorPageController时通过跨域过滤器
在跨域过滤器中重写 OncePerRequestFilter 的 shouldNotFilterErrorDispatch( )方法,让错误请求进入跨域过滤器设置跨域信息。
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
|