Nuxt - 每次路由切换后 asyncData / useFetch 跨域报错(但是刷新后会好

崧峻
原创
发布时间: 2025-06-10 02:43:58 | 阅读数 0收藏数 0评论数 0
封面
原因是 asyncData / useFetch 放入请求头后会触发option的跨域预检,向后端发送一个option的请求,如果后端用了拦截器过滤器 或者spring security之类的那么有可能把该请求拦截无法正常返回,让浏览器认为是跨域问题
1

options 请求


什么是 options 请求

options 请求就是预检请求,可用于检测服务器允许的 http 方法。当发起跨域请求时,由于安全原因,触发一定条件时浏览器会在正式请求之前自动先发起 OPTIONS 请求,即 CORS 预检请求,服务器若接受该跨域请求,浏览器才继续发起正式请求

options请求原因

这是因为在跨域的情况下,在浏览器发起"复杂请求"时主动发起的。跨域共享标准规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。
2

解决方法1 把NuxtLink添加上 external

根据官方文档 Using external forces the link to be rendered as an a tag instead of a Vue Router RouterLink 使用了这个之后就会让每次页面跳转都会像刷新页面一样,但是这样会大大降低 seo优化以及服务端渲染效率


  1. external:强制将链接呈现为<a>标签而不是 Vue Router RouterLink

例如:

<NuxtLink to="/user/123" external>
用户页面
</NuxtLink>


3

解决方法2 后端放行

让后端放行所有的options请求


如果用的是security的情况下使用这个
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS,"/**").permitAll();


如果用的是拦截器过滤器的话用这个代码
@Override

public void addInterceptors(InterceptorRegistry registry){

registry.addInterceptor(new HandlerInterceptor() {

//通过控制器之前执行的方法

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//判断请求方式,排除OPTIONS请求

if(request.getMethod().toUpperCase().equals("OPTIONS")){

return true;//通过所有OPTION请求

}

//其他业务拦截功能

return true;//返回true表示允许通过

}

}).addPathPatterns("/**")//添加拦截路径,拦截所有

.excludePathPatterns("/XXX/YYY/**");//排除拦截路径

}


4

推荐方法 提前拦截option请求

因为放行了所有的OPTIONS请求所以感觉会很不安全,然后我去查了一些资料,觉得还是方法2比较好,他会提前进行拦截判断是否为options预检请求,如果是的话人工返回200不让请求继续向下走


在security配置中中加入下面这个配置

// OPTIONS 预检测请求过滤
http.addFilterBefore(new OptionsFilter(), WebAsyncManagerIntegrationFilter.class).cors();


然后创建一个过滤器,判断是否为OPTIONS请求,如果是的话就返回200
/**
* 预检请求过滤器
*/
public class OptionsFilter implements Filter {
@Override
@SneakyThrows
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 判断请求是否为OPTIONS预检请求
if (req.getMethod().equals(HttpMethod.OPTIONS.name())) {
// 响应头
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "*");
resp.setHeader("Access-Control-Allow-Headers", "*");
// 本次预检请求的有效期,单位为秒,,在此期间不用再次发送预检请求。
resp.setHeader("Access-Control-Max-Age", "3600");
// 响应状态设置为200
resp.setStatus(HttpStatus.SC_OK);
return;
}
chain.doFilter(request, response);
}
}


阅读记录0
点赞0
收藏0
禁止 本文未经作者允许授权,禁止转载
猜你喜欢
评论/提问(已发布 0 条)
评论 评论
收藏 收藏
分享 分享
pdf下载 下载