如何使用JavaScript ajax调用在DialogFlow v2上进行HTTP调用

6

我在DialogFlow官网上找到了一个使用Node.js的示例,它可以正常工作,但是我不知道如何将其集成到我的Web应用程序中。

我能否将此与其他JavaScript jQuery代码集成?如果集成,是否仍需要运行node index.js命令?

const projectId = 'xxx'; //https://dialogflow.com/docs/agents#settings
const sessionId = 'xxxxx';
const query = 'Hello';
const languageCode = 'en-US';

// Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
const sessionClient = new dialogflow.SessionsClient();

// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
console.log(sessionPath);
// The text query request.
const request = {
  session: sessionPath,
  queryInput: {
    text: {
      text: query,
      languageCode: languageCode,
    },
  },
};

// Send request and log result
sessionClient
  .detectIntent(request)
  .then(responses => {
    console.log('Detected intent');
    const result = responses[0].queryResult;
    console.log(`  Query: ${result.queryText}`);
    console.log(`  Response: ${result.fulfillmentText}`);
    if (result.intent) {
      console.log(`  Intent: ${result.intent.displayName}`);
    } else {
      console.log(`  No intent matched.`);
    }
  })
  .catch(err => {
    console.error('ERROR:', err);
  });

有没有其他替代方案,可以使用普通的JavaScript jQuery、Ajax使用DialogFlow v2,而不需要每次想要使用DialogFlow时都执行node index.js。

使用DialogFlow v1非常简单。我像这样使用:

fetch(url, {
    body: JSON.stringify(data),
    // cache: 'no-cache',
    // credentials: 'same-origin',
    headers: {
        'content-type': 'application/json',
        "Authorization": "Bearer " + configs.accessToken,
    },
    method: 'POST',
    mode: 'cors',
    redirect: 'follow',
    referrer: 'no-referrer',
})
    .then(response => response.json()) // parses response to JSON

你想从API中获取什么? - Vivek
@Vivek,我想使用Dialogflow API V2来制作聊天机器人。 - Mizlul
6个回答

2
如其他人在这里之前所说,访问令牌的持续时间为一小时,之后它就变得无用了。因此,需要每小时调用一次API(在我的情况下是http调用)来请求访问令牌,并按照Satheesh的说明使用它。有关如何生成签名以进行调用并稍后使用的说明,请参见https://developers.google.com/identity/protocols/OAuth2ServiceAccount
一旦您从服务帐户中获取了具有私钥和要使用的电子邮件的json文件(不是您的电子邮件,而是由服务帐户生成的电子邮件),您可以使用jsrsasign库(纯JavaScript)来生成JSON Web Signature(JWS),从而生成JSON Web Token(JWT),这将需要进行http调用以获取访问令牌。您可以在https://github.com/kjur/jsrsasign中找到该库。
然后,您可以按照Satheesh上述的方式使用它来通过jQuery调用Dialogflow V2。
我用来实现这个的代码如下:
生成JWT(使用相关库)的步骤:
function _genJWS() {
    var header = '{"alg":"RS256","typ":"JWT"}';
    var claimSet = jwtClaimSet();
    var privateKey = jwtPrivateKey();

    var sHead = newline_toDos(header);
    var head = KJUR.jws.JWS.readSafeJSONString(sHead);
    var sPayload = newline_toDos(claimSet);
    var sPemPrvKey = privateKey;

    var jws = new KJUR.jws.JWS();
    var sResult = null;
    try {
        prv = KEYUTIL.getKey(sPemPrvKey);

        sResult = KJUR.jws.JWS.sign(head.alg, sHead, sPayload, prv);
    } catch (ex) {
        alert("Error: " + ex);
    }
    return sResult;
}

请求访问令牌:

function _requestAccessToken() {
    var access_token = accessToken;
    var assertion = _genJWS();
    console.log('Assertion: ' + assertion);
    jQuery.ajax({
        type: "POST",
        url: "https://www.googleapis.com/oauth2/v4/token",
        contentType: "application/x-www-form-urlencoded",
        dataType: "json",
        data: "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + assertion,
        success: function(response) {
            console.log("success");
            console.log(response);
            access_token = response.access_token;
            console.log(access_token);
        },
        error: function() {
            console.log("Error");
        }
    });
    return access_token;
}

然后使用该访问令牌进行HTTP调用Dialogflow。
希望能有所帮助。

你如何实现 jwtClaimSet 函数?除了这个,我已经想出了其他所有的东西(我想)。 - Venryx
没关系,我已经弄明白了(基于你的回答),并在这里发布了解决方案:https://dev59.com/Eavka4cB1Zd3GeqPzNFY#57857698 - Venryx
function jwtClaimSet() { var clst = ""; if (!Date.now) { Date.now = function() { return new Date().getTime(); } } timestampInSeconds = Math.floor(Date.now() / 1000); nextHourInSeconds = timestampInSeconds + (60*60); var iss = "your@gserviceaccount.com"; var scope = "https://www.googleapis.com/auth/dialogflow"; var aud = "https://www.googleapis.com/oauth2/v4/token"; var exp = nextHourInSeconds; var iat = timestampInSeconds; clst = JSON.stringify({ "iss":iss, "scope":scope, "aud":aud, "exp":exp, "iat":iat }); return clst; } - JDE10
我已将所有3个函数复制到我的JavaScript文件中,并在HTML中包含了jsrsasign库的脚本标签,如下所示:<script src="https://cdnjs.cloudflare.com/ajax/libs/jsrsasign/8.0.20/jsrsasign-all-min.js"></script> 但是出现了错误,提示jwtPrivateKey未找到。看起来这个库中的函数对于js文件不可见。@JDE10,请问我需要做什么? - jkr
我正在浏览器/客户端上运行这整个(完全基于JavaScript的)代码。没有使用Node/服务器。 - jkr
从另一个链接解决了问题。谢谢。 - jkr

1
你可以轻松地从jQuery调用Dialogflow的V2 API detectIntent端点。 API文档显示了URL和请求格式:
POST https://dialogflow.googleapis.com/v2/{session=projects/*/agent/sessions/*}:detectIntent

{
  "queryParams": {
    object(QueryParameters)
  },
  "queryInput": {
    object(QueryInput)
  },
  "inputAudio": string
}

身份验证的工作方式略有不同;您将使用Cloud仪表板创建服务帐户和密钥,而不是使用访问令牌。此文档页面进行了解释。


2
我已经创建了服务账号和密钥,如何使用它们来发出你展示的 HTTP/AJAX 请求? Authorization: Bearer $(gcloud auth print-access-token) 对我来说并不简单,请您能给出一些示例吗? - Mizlul
2
我不知道如何在jquery中使用我的服务账户密钥,也不知道如何进行身份验证以便使用Dialogflow API。 - Mizlul
1
同样的问题,没有任何关于使用V2 API的教程。真是难以置信!已经浪费了两个星期... - Federico Schiocchet
@Mizlul 和 Federico,我已经弄清楚了(基于JDE10的答案),并在这里发布了解决方案:https://dev59.com/Eavka4cB1Zd3GeqPzNFY#57857698 - Venryx

0

https://dialogflow.com/docs/reference/v2-auth-setup

在按照上述页面并创建服务密钥后,您必须拥有一个类似于以下的JSON文件:

{
    "type": "",
    "project_id": "",
    "private_key_id": "",
    "private_key": "",
    "client_email": "",
    "client_id": "",
    "auth_uri": "",
    "token_uri": "",
    "auth_provider_x509_cert_url": "",
    "client_x509_cert_url": ""
  }  

使用此文件,执行以下两个命令(如果尚未安装gcloud CLI,则需要安装):
gcloud auth activate-service-account --key-file=<service-account-key-file.json>
gcloud auth print-access-token

在那之后,复制访问令牌并将其作为环境变量提供给您的程序(或硬编码,如果您不关心)。然后,您只需要进行HTTP post请求。我在我的React项目中使用axios,这里是detectIntent的样例代码:

import axios from "axios";

const DIALOG_FLOW_TOKEN = process.env.REACT_APP_DIALOG_FLOW_TOKEN;
const DIALOG_FLOW_API_ROOT_URL = "https://dialogflow.googleapis.com/v2";
const YOUR_PROJECT_ID = <YOUR_PROJECT_ID>;
const SESSION_ID = <SESSION_ID>;
const URL = `${DIALOG_FLOW_API_ROOT_URL}/projects/${YOUR_PROJECT_ID}/agent/sessions/${SESSION_ID}:detectIntent`;

var config = {
    headers: {
        "Authorization": "Bearer " + DIALOG_FLOW_TOKEN,
        "Content-Type": "application/json"
    }
};

export function sendText(text) {
    var bodyParameters = {
        "queryInput": { "text": { "text": text, "languageCode": "en" } },
    };

    const request = axios.post(
        URL,
        bodyParameters,
        config
    );

    return request;
} 

访问令牌在一小时后过期,您该如何处理? - Mizlul
是的,你说得对,抱歉我的错误。但我找到了这个答案,可能会有帮助,你看过了吗? https://dev59.com/j1YN5IYBdhLWcg3wsZ1R - Barış Deniz Sağlam

0
下面的代码片段解释了如何从Web应用程序与Dialogflow NLU进行通信。要获取访问令牌,我们可以使用Google Cloud SDK来获取令牌,但它的有效期为一小时,因此将其放在不同的服务中并在调用Dialogflow之前获取它将提供一个解决方法。

$(document).ready(function(){

$("button").click(function(){

    $.ajax({
        type: "POST",
        url: "https://dialogflow.googleapis.com/v2/projects/YOUR-PROJECT-NAME/agent/sessions/SESSIONIDOFYOURCHOICE:detectIntent",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        headers: {
            "Authorization": "Bearer " + "YOUR ACCESS TOKEN GOES HERE",
        },
        data: JSON.stringify({ "queryInput":{
            "text":{
                "text":"YOUR QUERY TO NLU",
                "languageCode":"en-US"
            }
        } }),
        success: function(data) {
            $("#div").html(data.queryResult.fulfillmentText);
        },
        error: function() {
            console.log("Internal Server Error");
        }
    });     

});

});


但是如何从“不同的服务”(例如云函数)生成令牌呢?https://developers.google.com/identity/protocols/OAuth2ServiceAccount 和 googleapis npm 模块似乎是一个不错的起点,但我还没有找到关于如何生成这些访问令牌的清晰说明。 - Doug
我仍然没有足够的互动以提高我的声誉并留下评论。这只是为了在Doug问如何生成访问令牌时给一些提示。在我的情况下,它位于Dialogflow控制台本身内,在代理配置文件中 - 单击代理名称旁边的齿轮 - 在“常规”选项卡下。 实际上我有两个访问令牌,一个客户端访问令牌和一个开发者访问令牌。我在某个地方读到需要开发人员访问令牌来解决detectIntent问题。希望能有所帮助。 - JDE10
@Doug 我已经弄清楚了(基于JDE10的答案),并在这里发布了解决方案:https://dev59.com/Eavka4cB1Zd3GeqPzNFY#57857698 - Venryx

0

我找到了一种方法,可以完全从前端(浏览器)JavaScript中获取有效的访问令牌。

1)从npm安装npm包jsrsasign

2)将此Gist中的文件添加到您的项目中:https://gist.github.com/Venryx/bfa9b69583638dfbf611a9f292721a75

3)在DialogFlowExample.ts开头为所有常量输入值。

您的服务帐户信息可以在此处找到:https://console.cloud.google.com/iam-admin/serviceaccounts

服务帐户的电子邮件列在表格中,如果您已创建,则其私钥在下载的密钥JSON文件中找到。如果您不记得放置密钥JSON文件的位置(或尚未创建),则可以使用“操作->创建密钥”来创建新密钥。

4) 使用您的声明集和私钥(请参见Gist示例)调用GetAccessToken。它将返回一个Promise。

5) 一旦检索到访问令牌,Promise将解决并返回检索到的访问令牌。(该函数将访问令牌缓存60分钟。在此期间,令牌已过期,因此再次调用该函数将使其检索新令牌。)

示例

链接的Gist包含一个几乎完整的使用示例,用于使用DialogFlow将音频片段转换为文本。它没有显示如何获取“音频片段”的部分。

有关详细信息,请参见我的答案的第1部分:https://stackoverflow.com/a/57859776/2441655


这是指您将服务帐户的私钥暴露给任何人的互联网浏览器吗? - system programmer
1
@systemprogrammer 是的,没错。因此,我不建议在生产中使用它来进行大型项目,因为恶意用户可能会使用您的服务密钥创建虚假的转录调用,从而耗尽您的配额。话虽如此,在较小的项目(或不太可能吸引恶意用户的项目)中,这种基于浏览器的方法对于只有少数人(或受信任的一组人)将使用的东西来说,设置服务器组件作为中间人是不必要的麻烦。(如果发生了这种情况,您需要使密钥失效,更改代码,然后创建一个新的密钥)。 - Venryx

-1

这种方式对我很有效。

const dialogflow = require('dialogflow');
var projectId = ""; // your dialogflow project id when you click on settings of the DF agent
var sessionId = ""; // session ID
var config = {
// use credentials or keyFilename i'm using keyFile
    // credentials: {
        // private_key: privateKey,
        // client_email: clientEmail,
    // }
        keyFilename: './google.json'
};

var sessionClient = new dialogflow.SessionsClient(config);

async sendTextMessageToDialogFlow(textMessage) {
        // Define session path
        const sessionPath = sessionClient.sessionPath(projectId, sessionId);
        // The text query request.
        const request = {
            session: sessionPath,
            queryInput: {
                text: {
                    text: textMessage,
                    languageCode: 'en'
                }
            }
        }
        try {
            let [responses] = await sessionClient.detectIntent(request)
            console.log(responses);
        } catch (err) {
            console.error('DialogFlow.sendTextMessageToDialogFlow ERROR:', err);
            throw err
        }
    };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接