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