vue+axios中使用raw+json请求失败的解决办法,application/json请求跨域问题

本文介绍了解决后端跨域问题的方法,通过设置Access-Control-Allow-Origin等响应头来实现跨域资源共享(CORS)。同时强调了Content-Type权限的重要性,并提供了过滤器代码示例。

后端需要开启Content-Type的权限,平时不知道没开启权限,到自己遇到找了好久才知道是这个搞得鬼。o(╥﹏╥)o

 //Filter过滤器跨域处理
        String origin = request.getHeader("Origin");
        response.setHeader("Access-Control-Allow-Origin", origin);
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization,token,**Content-Type**");
        response.setHeader("Access-Control-Allow-Credentials", "true");

附上过滤器的代码。

@WebFilter(urlPatterns = {"/*"})//过滤所有URL
public class LoginFilter implements Filter {

    //ALLOWED_PATHS 存放不需要过滤的url
    private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(
            Arrays.asList("/login", "/logout")));

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("----Filter过滤器初始化----");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //校验用户登录状态
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", "");
        boolean allowedPath = ALLOWED_PATHS.contains(path);

        //若该url不需要过滤
        if(allowedPath){
            //放行
            filterChain.doFilter(servletRequest,servletResponse);
        } else{

        //Filter过滤器跨域处理
        String origin = request.getHeader("Origin");
        response.setHeader("Access-Control-Allow-Origin", origin);
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization,token,Content-Type");
        response.setHeader("Access-Control-Allow-Credentials", "true");

        //获取Headers中的参数
        String token = request.getHeader("token");
        token = token == null ? "" : token;

        //查询token在Redis中的剩余时间
        Long expire = redisTemplate.getExpire(token);
        if(expire > 0){ //是登录状态
            //重置token的时间
            redisTemplate.expire(token,30L, TimeUnit.MINUTES);

            //放行
            filterChain.doFilter(servletRequest,servletResponse);
        }else{
            //未登录,响应数据
            JSONObject jsonObject = new JSONObject();
            //jsonObject.put("user","null");
            jsonObject.put("msg","未登录");
            String string = JSONObject.toJSONString(jsonObject);
            response.setContentType("json/text;charset=utf-8");
            PrintWriter out = response.getWriter();

            out.write(string);
        }
        }
    }

    @Override
    public void destroy() {
        System.out.println("----过滤器销毁----");
    }
}

<think> 我们使用Vue2和Element UI来构建一个对话窗口,同时使用axios进行流式请求使用SSE或WebSocket,但Dify平台支持SSE,因此我们使用EventSource或axios的流式响应)。不过,由于Dify平台API通常使用流式响应(chunked response),我们可以使用axios来获取流,然后逐步显示数据。 注意:由于Dify平台API的流式响应通常是text/event-stream,我们可以使用EventSource,但考虑到和认证,使用axios更灵活(因为可以设置headers)。但是,axios默认不支持流式响应(直到axios 0.21.0版本开始支持responseType: 'stream',但在浏览器中不支持,浏览器中通常使用Fetch API的ReadableStream)。因此,在浏览器环境中,我们通常使用fetch或者EventSource。 这里我们假设Dify平台使用的是SSE(Server-Sent Events),所以我们可以使用EventSource。但是,EventSource不支持自定义header(如认证token),所以如果接口需要认证,我们可以使用Fetch API或者使用一个polyfill(如eventsource)来支持自定义header。然而,在浏览器中,我们通常使用Fetch API来处理。 由于要求使用axios,但是axios在浏览器中无法直接处理流式响应(分块),所以我们将使用Fetch API来实现流式读取,这样更简单直接。如果项目必须使用axios,我们可以考虑使用axios来发送请求,但是处理流的话,axios在浏览器环境不支持。因此,我们选择使用Fetch API。 我们将创建一个简单的对话界面,包含: 1. 一个消息列表(用于显示用户和智能体的对话) 2. 一个输入框(用于用户输入) 3. 一个发送按钮 当用户发送消息时,我们将消息添加到消息列表,然后向Dify平台发送请求使用流式接收响应,并实时将智能体的回答显示在消息列表中。 步骤: 1. 使用Vue2和Element UI搭建界面。 2. 使用Fetch API向Dify的流式API发送请求,并处理返回的流。 3. 将接收到的数据实时显示。 注意:Dify的流式API文档中提到的端点和使用方法,这里假设我们已经知道API的URL和必要的参数(如API Key等)。 由于Dify的API要求,我们需要在请求头中设置认证信息(如Bearer Token),并且请求体为JSON格式,包含输入参数(如query等)。 我们将使用Dify的Web API流式接口:POST /v1/completion-messages(或者/v1/chat-messages,具体看Dify的版本)。具体请参考Dify文档。 下面是一个示例实现: 首先,安装必要的依赖:vue2, element-ui, axios(虽然这里使用fetch,但axios用于其他请求也可以)。 代码结构: - 组件:ChatWindow.vue 在组件中,我们将实现: - data: 包含messages(消息数组),inputMessage(输入框绑定) - methods: sendMessage() 用于发送用户消息,并调用fetchStream方法获取智能体回复。 fetchStream() 使用fetch API发起流式请求,并处理流。 由于流式响应是多个事件(event)或者分块(chunks),我们需要逐步拼接并更新最后一条消息(智能体的消息)的内容。 具体代码实现如下: </think> 下面我将为您实现一个基于Vue2+ElementUI+Axios的智能体对话窗口,实时流式获取Dify平台的响应。以下是完整解决方案: ### 解决方案思路 1. **前端组件**:使用ElementUI的Card、Input、Button等组件构建聊天界面 2. **流式请求**:利用axios的`onDownloadProgress`事件处理流式响应 3. **数据处理**:实时拼接数据块并更新DOM,实现打字机效果 ### 完整代码实现 ```vue <template> <div class="chat-container"> <el-card class="chat-card"> <!-- 消息显示区 --> <div ref="chatWindow" class="chat-window"> <div v-for="(msg, index) in messages" :key="index" :class="['message', msg.role]" > <div class="avatar"> <el-avatar :src="msg.role === 'user' ? userAvatar : botAvatar" size="medium" /> <span class="name">{{ msg.role === 'user' ? '我' : '智能体' }}</span> </div> <div class="content"> <div v-if="msg.role === 'assistant' && index === messages.length - 1"> <!-- 最后一条消息实时更新 --> <span v-html="formatContent(lastMessageContent)"></span> <span v-if="isLoading" class="cursor"></span> </div> <div v-else> {{ msg.content }} </div> </div> </div> </div> <!-- 输入区 --> <div class="input-area"> <el-input v-model="inputMessage" placeholder="请输入您的问题..." :disabled="isLoading" @keyup.enter.native="sendMessage" > <el-button slot="append" type="primary" :loading="isLoading" @click="sendMessage" >发送</el-button> </el-input> </div> </el-card> </div> </template> <script> import axios from 'axios'; export default { name: 'ChatAgent', data() { return { messages: [], // 对话消息数组 inputMessage: '', // 用户输入消息 isLoading: false, // 加载状态 lastMessageContent: '', // 实时更新的最后消息内容 userAvatar: require('./assets/user.png'), // 用户头像 botAvatar: require('./assets/bot.png'), // 智能体头像 difyConfig: { apiKey: 'YOUR_DIFY_API_KEY', // 替换为实际API Key endpoint: 'https://api.dify.ai/v1/completion-messages' // Dify接口地址 } }; }, methods: { // 发送消息到Dify async sendMessage() { if (!this.inputMessage.trim() || this.isLoading) return; const userMessage = this.inputMessage.trim(); // 添加用户消息 this.messages.push({ role: 'user', content: userMessage }); // 准备请求数据 const payload = { inputs: {}, query: userMessage, response_mode: "streaming", // 启用流式响应 user: "user123" // 用户标识(可选) }; this.inputMessage = ''; this.isLoading = true; this.lastMessageContent = ''; // 添加初始的智能体消息占位 this.messages.push({ role: 'assistant', content: '' }); try { // 发送流式请求 const response = await axios.post( this.difyConfig.endpoint, payload, { headers: { 'Authorization': `Bearer ${this.difyConfig.apiKey}`, 'Content-Type': 'application/json' }, // 流式处理关键配置 onDownloadProgress: (progressEvent) => { const chunk = progressEvent.currentTarget.responseText; this.processStreamData(chunk); } } ); } catch (error) { console.error('API请求错误:', error); this.messages[this.messages.length - 1].content = '请求失败: ' + error.message; } finally { this.isLoading = false; } }, // 处理流式数据 processStreamData(rawData) { /* Dify流式响应格式示例: event: message data: {"answer": "Hi", ...} event: message data: {"answer": " there", ...} event: done data: {"task_id": ...} */ // 按行分割数据 const lines = rawData.split('\n\n'); lines.forEach(line => { if (line.startsWith('data: ')) { try { const jsonStr = line.replace('data: ', ''); const dataObj = JSON.parse(jsonStr); // 拼接回答内容 if (dataObj.answer) { this.lastMessageContent += dataObj.answer; // 更新最后一条消息的内容 this.messages[this.messages.length - 1].content = this.lastMessageContent; // 滚动到底部 this.$nextTick(() => { this.scrollToBottom(); }); } } catch (e) { console.warn('解析JSON失败:', e); } } }); }, // 格式化内容(换行处理) formatContent(content) { return content.replace(/\n/g, '<br>'); }, // 滚动到底部 scrollToBottom() { const container = this.$refs.chatWindow; container.scrollTop = container.scrollHeight; } } }; </script> <style scoped> .chat-container { max-width: 800px; margin: 0 auto; padding: 20px; } .chat-card { height: 70vh; display: flex; flex-direction: column; } .chat-window { flex: 1; overflow-y: auto; padding: 15px; background-color: #f9f9f9; border-radius: 4px; margin-bottom: 15px; } .message { margin-bottom: 20px; } .message.user { text-align: right; } .avatar { display: flex; align-items: center; margin-bottom: 8px; } .message.user .avatar { justify-content: flex-end; } .name { margin: 0 10px; font-weight: bold; } .content { padding: 12px 15px; border-radius: 4px; display: inline-block; max-width: 80%; } .message.user .content { background-color: #e1f0fa; text-align: left; } .message.assistant .content { background-color: #f1f0f0; } .input-area { margin-top: 10px; } .cursor { display: inline-block; width: 8px; height: 16px; background-color: #333; margin-left: 2px; animation: blink 1s infinite; } @keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } } </style> ``` ### 代码核心要点解释 1. **流式请求处理** - 使用axios的`onDownloadProgress`回调处理分块响应 - 通过`progressEvent.currentTarget.responseText`获取实时数据 - 按照Dify的SSE格式解析数据 (`data: {...}`) 2. **实时更新机制** - 使用`lastMessageContent`变量存储当前回答的累积内容 - 每次收到新数据块时更新最后一条消息的content - 通过`$nextTick`确保DOM更新后滚动到底部 3. **Dify接口规范** - 请求头必须包含`Authorization: Bearer API_KEY` - 请求体设置`response_mode: "streaming"` - 响应格式为text/event-stream,每行以`data: `开头 4. **用户体验优化** - 打字机效果的闪烁光标(CSS动画) - 自动滚动保持最新消息可见 - 加载状态禁用输入框并显示加载动画 ### 部署注意事项 1. **问题**:确保后端配置CORS或使用代理 2. **API密钥安全**:实际项目中应通过后端中转请求避免暴露密钥 3. **性能优化**:大流量场景建议添加消息节流和错误重试机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值