1.SSE 原理机制
sse 类似websocket,但是sse是单向的,不可逆的,只能服务端向客户端发送数据流
2.解决跨域问题
Access to XMLHttpRequest at 'http://127.0.0.1:8090/sse/doChat' from origin 'http://127.0.0.1:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
request.js:48err: Error: Network Error
require.js 文件
// 创建axios实例
const instance = axios.create({// baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url// baseURL: "http://192.168.3.110:8090", // url = base url + request urlbaseURL: "http://127.0.0.1:8090", // url = base url + request url// baseURL: "http://150.109.247.64:9090",// baseURL: "https://www.itzixi.com/api", // url = base url + request urlwithCredentials: true, // send cookies when cross-domain requeststimeout: 60000 // request timeout
});// axios请求的拦截器
instance.interceptors.request.use(config => {// do something before request is sentvar userInfo = cookieUtils.getUserInfo();// console.log(userInfo);if (userInfo) {// console.log("userId = " + userInfo.id);config.headers['headerUserId'] = userInfo.id;}var userToken = cookieUtils.getToken();// console.log("userToken = " + userToken);if (userToken) {// console.log("userToken = " + userToken);config.headers['headerUserToken'] = userToken;}return config},error => {// do something with request errorconsole.log(error) // for debugreturn Promise.reject(error)}
);// axios响应的拦截器
instance.interceptors.response.use(response => {const res = response.datareturn res;},error => {console.log('err: ' + error) // for debugconsole.log('err: ' + error.data) // for debugreturn Promise.reject(error)}
)
MvcConfiguration.java 文件如下
package com.spring.springai.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MvcConfiguration implements WebMvcConfigurer {/*** 解决跨域* addMapping:设置哪些接口支持跨域* allowedOrigins:设置跨域的来源,也就是哪些域名最终可以接收响应* allowedMethods:设置支持跨域的方法* allowHeaders:运行哪些请求头* @param registry*/public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 允许所有接口支持跨域.allowedOrigins("*")// 允许所有来源.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")// 允许所有HTTP方法.allowedHeaders("*");// 允许所有请求头
// .allowCredentials(true); // 允许发送Cookie,需要指定具体的origin}
}
这个错误是因为你的前端请求设置了 withCredentials: true
(即包含凭据,如 cookies、HTTP认证等),但后端 CORS 配置使用了通配符 *
作为 allowedOrigins
,这是不被允许的。
根据 CORS 策略,当请求包含凭据时,服务器不能使用 Access-Control-Allow-Origin: *
,而必须指定明确的域名。
你有两种解决方案:
方案1:前端移除 withCredentials(如果不需凭据)
如果你的跨域请求不需要携带 cookies 或认证信息,可以在前端代码中移除 withCredentials
设置:
// 如果是使用 axios
axios.get('http://127.0.0.1:8090/sse/doChat', {withCredentials: false // 或者直接移除这个配置
})// 如果是原生 XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.withCredentials = false; // 设置为 false
方案2:后端指定具体域名并启用 allowCredentials(推荐)
如果你需要携带凭据,修改后端的 CORS 配置:
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("http://127.0.0.1:3000") // 指定前端具体域名.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").allowedHeaders("*").allowCredentials(true); // 允许凭据}
}
方案3:使用 allowedOriginPatterns(更灵活)
Spring Framework 5.3+ 支持使用模式匹配:
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOriginPatterns("http://127.0.0.1:*", "http://localhost:*") // 使用模式匹配.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").allowedHeaders("*").allowCredentials(true);}
}
方案4:使用 Filter 进行更精细的控制
@Configuration
public class CorsConfig {@Beanpublic CorsFilter corsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration config = new CorsConfiguration();config.setAllowCredentials(true);config.addAllowedOrigin("http://127.0.0.1:3000");config.addAllowedHeader("*");config.addAllowedMethod("*");source.registerCorsConfiguration("/**", config);return new CorsFilter(source);}
}
对于 SSE (Server-Sent Events) 的特殊处理
如果你使用的是 SSE,还需要确保正确处理 OPTIONS 预检请求:
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/sse/**") // 单独配置 SSE 端点.allowedOrigins("http://127.0.0.1:3000").allowedMethods("GET", "POST", "OPTIONS").allowedHeaders("*").allowCredentials(true).maxAge(3600); // 预检请求缓存时间}
}
推荐使用方案2或方案3,指定具体的前端域名并启用 allowCredentials(true)
,这样既能保证安全性,又能满足携带凭据的需求。
最终解决方法:
public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 允许所有接口支持跨域//.allowedOrigins("*")// 允许所有来源.allowedOrigins("http://127.0.0.1:3000", "http://localhost:3000", "http://ai.mywoshop.com:3000").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")// 允许所有HTTP方法.allowedHeaders("*")// 允许所有请求头.allowCredentials(true); // 允许发送Cookie,需要指定具体的origin}
前端代码:https://gitee.com/yang-jiayu12/springai-mcp-front-end
后端代码:https://gitee.com/yang-jiayu12/springai-mcp