Angular调用Google Dialogflow API遭遇CORS问题,求解决方案
问题
在Google Dialogflow中创建意图后,尝试在Angular应用中调用detectIntent接口,使用api-ai-javascript的ApiAiClient发起请求,OAuth2.0身份验证正常,但调用时出现CORS错误:
Access to XMLHttpRequest at 'https://asia-south1-dialogflow.googleapis.com/****:detectIntentquery?v=20150910' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
使用Chrome插件绕过CORS后,仍出现:
Access to XMLHttpRequest at 'https://asia-south1-dialogflow.googleapis.com/****:detectIntentquery?v=20150910' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
需求:无需Chrome插件即可正常调用API,询问是否可通过Dialogflow配置解决CORS,或是否需要API网关添加响应头。
Angular调用代码如下:
import { Injectable } from '@angular/core'; import { environment } from './environments/environment'; import { ApiAiClient } from 'api-ai-javascript/es6/ApiAiClient'; import { ApiAiConstants, IApiClientOptions, IRequestOptions,IServerResponse } from 'api-ai-javascript'; import { Observable } from 'rxjs/internal/Observable'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; // Message class for displaying messages in the component export class Message { constructor(public content: string, public sentBy: string) {} } @Injectable() export class ChatService { readonly token = environment.dialogflow.angularBot; readonly url = environment.dialogflow.url; readonly apiVersion = environment.dialogflow.version; options:IApiClientOptions = { accessToken: this.token, baseUrl:this.url , } client = new ApiAiClient(this.options); conversation = new BehaviorSubject<Message[]>([]); constructor() {} // Sends and receives messages via DialogFlow converse(msg:string) { const userMessage = new Message(msg, 'user'); this.update(userMessage); console.log(msg); return this.client.textRequest(msg).then((res) => { const speech = res.result.fulfillment.speech; const botMessage = new Message(speech, 'bot'); this.update(botMessage); }).catch(err =>{ console.log(err.message); }) }; // Adds message to source update(msg: Message) { this.conversation.next([msg]); } }
解决方案
1. 无法通过Dialogflow直接配置解决CORS问题
Dialogflow的v2及以上版本的detectIntent API不支持直接从浏览器端调用,Google未开放CORS权限给前端域名,这是出于安全考虑(避免API密钥或OAuth令牌泄露),因此无法通过Dialogflow控制台的任何配置绕过该限制。
2. 推荐的两种可行方案
方案A:后端代理请求
这是最安全且通用的方案:
- 在你的后端服务(如Node.js、Java、Python等)中集成Dialogflow的官方Client Library
- 前端Angular应用将用户消息发送到自己的后端接口
- 后端接口调用Dialogflow的
detectIntentAPI,处理响应后返回给前端 - 后端调用无CORS限制,同时可以保护API密钥/令牌不暴露给前端
方案B:使用Dialogflow Messenger官方组件
Google提供了专门的前端集成组件Dialogflow Messenger,它是官方允许的前端直接交互方式,不会触发CORS问题:
- 只需在Angular组件中嵌入官方提供的
<df-messenger>标签 - 配置你的Dialogflow代理ID即可快速实现聊天功能
- 无需自行处理API调用和CORS,官方已做好适配
3. API网关方案(可选)
如果一定要通过前端直接调用API,可以使用Cloud API Gateway(或其他第三方网关)作为中间层:
- 在网关中配置转发规则,将前端请求转发到Dialogflow的
detectIntentAPI - 在网关层面添加CORS响应头:
Access-Control-Allow-Origin(指定你的前端域名,如http://localhost:4200)、Access-Control-Allow-Methods、Access-Control-Allow-Headers等 - 注意在网关中配置身份验证(如OAuth2.0或API密钥),避免未授权访问和密钥泄露
内容的提问来源于stack exchange,提问作者Ashok Ambrose

