React中使用Fetch API无法捕获Spring Security返回的HTTP 401错误
嘿,我一眼就揪出问题的根源了——你设置的mode: "no-cors"正是导致你拿不到响应状态码、catch也完全不生效的罪魁祸首!
为什么mode: "no-cors"会搞砸一切?
当你开启这个模式时,浏览器会严格限制你对响应的访问权限:不管服务器返回什么状态码(包括401),你都没法读取response.status、响应内容这些关键信息;而且Fetch的catch块只会在网络层面出错(比如请求根本发不出去)时才触发,像401这种HTTP状态码错误,它会认为请求“成功完成”,自然不会走到catch里。
一步步解决问题:
1. 立刻移除mode: "no-cors"配置
直接删掉Fetch选项里的这行代码,浏览器默认会使用cors模式,这才允许你正常读取响应内容。但前提是你的Spring Security后端要配置好CORS,允许前端域名发起跨域请求。
2. 给Spring Security配置CORS支持
在你的Spring Security配置类里开启CORS,并设置允许的前端域名、请求方法等规则:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .cors() // 开启CORS支持 .and() .authorizeRequests() // 这里放你的其他安全规则,比如允许登录接口匿名访问 .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginProcessingUrl("/login"); } // 配置具体的CORS规则 @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); // 替换成你的前端域名,比如http://localhost:3000 config.setAllowedOrigins(Arrays.asList("http://你的前端地址")); config.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS")); config.setAllowedHeaders(Arrays.asList("Content-Type")); config.setAllowCredentials(true); // 如果需要携带cookie的话开启 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; } }
3. 修改前端Fetch代码,正确处理HTTP错误
Fetch不会自动把4xx/5xx状态码当成错误,所以你需要在第一个then里手动判断状态,把非2xx的状态转为错误,这样才能进入catch块处理:
login = (event) => { event.preventDefault(); const data = { username: this.refs.username.value, password: this.refs.password.value }; fetch('http://localhost:8080/login', { method: 'POST', body: JSON.stringify(data), headers: { 'Content-Type': 'application/json' }, credentials: 'include' // 如果需要携带认证相关cookie,加上这行 }) .then(response => { // 先判断状态是否在2xx成功范围内 if (!response.ok) { // 抛出包含状态码的错误,方便后续处理 return Promise.reject(new Error(`登录失败!状态码:${response.status}`)); } return response; }) .then(() => { console.log('Yeeee man, you can login'); this.props.history.push('/'); }) .catch(error => { console.log('错误信息:', error.message); // 这里可以根据状态码做针对性提示,比如401就提示用户名或密码错误 }); }
额外提醒
如果你的前端和后端是同域的(比如都在localhost:8080),那CORS配置可以省略;但如果是跨域(比如前端在3000端口,后端在8080),必须配置CORS,否则浏览器会直接拦截请求。
内容的提问来源于stack exchange,提问作者Daimon
相关产品推荐
相关产品推荐

