“格式错误的响应:由于语音响应为空,无法将Dialogflow响应解析为AppResponse”。在OAuth帐户关联流程中 [英] "MalformedResponse: Failed to parse Dialogflow response into AppResponse because of empty speech response" during OAuth Account Linking flow

查看:134
本文介绍了“格式错误的响应:由于语音响应为空,无法将Dialogflow响应解析为AppResponse”。在OAuth帐户关联流程中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在使用新发布的



该操作已配置为使用我们自己的OAuth 2兼容模拟基础结构进行帐户链接。





只有一个意图(称为 RawText )在Dialogflow中配置,其余的交互将由自己的内部应用程序通过其Web挂钩处理。





状态机的状态如下到目前为止,已使用Java进行编码:

 公共类AoGApp扩展了DialogflowApp {

private final static Logger log = LoggerFactory.getLogger(AoGApp.class);

public static final String GREETING = GOOGLE_ASSISTANT_WELCOME;

@ForIntent( RawText)
//@ForIntent(\"actions.intent.MAIN)
public ActionResponse launchRequestHandler(ActionRequest request){

字符串userId = request.getAppRequest()。getUser()。getUserId();
log.info( userId = {},userId);
字符串queryText = request.getWebhookRequest()。getQueryResult()。getQueryText();
log.info( queryText = {},queryText);


字符串语音= null;

ResponseBuilder responseBuilder = getResponseBuilder(请求);

if(isBlank(userId)|| GREETING.equalsIgnoreCase(queryText)){

speech = \nHi。我感觉到您未来的银行经验很棒,我看到您的帐户未连接。
+我已将链接发送到您的Google Assistant应用,只需几个简单的步骤即可开始设置。
+唐不用担心,我会在这里等您准备就绪时召唤我。


responseBuilder.add(
new SignIn()
.setContext(speech));
} else {
speech =欢迎。您可以打个招呼。;
responseBuilder.add(speech);
}


return responseBuilder.build();
}

@ForIntent( actions.intent.SIGN_IN)
public ActionResponse getSignInStatus(ActionRequest request){
ResponseBuilder responseBuilder = getResponseBuilder(request);
字符串文本=来自登录处理程序的问候;
responseBuilder.add(text);
log.info(文本);
返回responseBuilder.build();

}

}


和相关的HttpRequest处理:

@Override
受保护的无效handlePOST(最终请求请求,最终HttpServletResponse响应){

try {
String rawRequest = ControllerUtils.toString(request.getReader());


字符串jsonResponse = app.handleRequest(rawRequest,getHeadersMap(request))。get();
log.info( Generated response:\n {},ControllerUtils.prettyPrint(jsonResponse));
response.setContentType(APPLICATION_JSON.getMimeType());
response.getWriter()。write(jsonResponse);
} catch(异常e){
handleError(response,e);
}
}






 公共最终类ControllerUtils {

private final static静态Logger日志= LoggerFactory.getLogger(ControllerUtils.class);
私有静态ObjectMapper映射器= new ObjectMapper();

公共静态字符串toString(BufferedReader reader)引发异常{

String rawRequest = reader
.lines()
//.map(e- > e.concat(System.lineSeparator()))
.collect(Collectors.joining(System.lineSeparator()));
log.info( Received AoG Request {},rawRequest);
返回rawRequest;
}

公共静态字符串prettyPrint(String json)引发异常{
return mapper.writerWithDefaultPrettyPrinter()。writeValueAsString(mapper.readValue(json,Object.class)));
}

公共静态Map< String,String> getHeadersMap(org.eclipse.jetty.server.Request jettyRequest){
return Collections.list((Enumeration< String>)jettyRequest.getHeaderNames())
.stream()
.collect( Collectors.toMap(
name-> name,
jettyRequest :: getHeader));

}


}

按照上述配置,OAuth授权代码流程将执行正常的OAuth 2步骤:




  • hits /登录端点以提供凭据


  • 命中 / token 端点以获得令牌(其值在以下日志中为 token1 。我们具有生成和注入自己的令牌的功能,这是一个测试环境,因此我们生成了此 token1 值,该值似乎已成功合并到后续请求中。)




下面是失败的互动的详细屏幕快照,以及由Google控制台上的动作提供的附件日志:



  [
{
textPayload:发送请求带有帖子数据的t:{\ user\:{\ userId\:\ ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9eg\:\local \ en-US\,\ lastSeen\:\ 2019-02-20T21:32:22Z\,\ userStorage\:\ {\\\ \ data\\\:{}} \},\ conversation\:{\ conversationId\:\ ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHdv type, :\ NEW\},\输入\:[{\ intent\:\ actions.intent.MAIN\,\ rawInputs\:[ {\ inputType\:\ VOICE\,\ query\:\开放语音库\}]}}],\ surface\:{\ capabilities\:[{\ name\:\ actions.capability.AUDIO_OUTPUT\},{\ name\:\ actions.capability.MEDIA_RESPONSE_AUDIO\} ,{\ name\:\ actions.capability.SCREEN_OUTPUT\},{\ name\:\ actions.capability.WEB_BROWSER\}]},\ isInSandbox\:true,\ availableSurfaces\:[{\ capabilities\:[{\ name\:\ actions.capability.AUDIO_OUTPUT\},{\ \ name\:\ actions.capability.SCREEN_OUTPUT\},{\ name\:\ actions.capabili ty.WEB_BROWSER\}]}]}。,,
insertId: f9fzrtf3hjgn4,
资源:{
type: assistant_action,
标签:{
project_id: speechbank-e8a15,
version_id:,
action_id: actions.intent.MAIN
}
},
timestamp: 2019-02-21T13:47:56.713587946Z,
severity: DEBUG,
labels:{
channel:预览,
源: AOG_REQUEST_RESPONSE,
querystream: GOOGLE_USER
},
logName:项目/speechbank-e8a15/logs/actions.googleapis.com%2Factions
一丝: 项目/ 366800784520 /踪迹/ ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug,
receiveTimestamp: 2019-02-21T13:47 :57.205496026Z
},
{
textPayload:已收到来自主体的代理的响应:HTTP / 1.1 200 OK\r\nServer:nginx / 1.13.6\ r\n日期:星期四,2019年2月21日13:47:57 GMT\r\n内容类型:application / j son; charset = UTF-8\r\nContent-Length:426\r\nX-Cloud-Trace-Context:d8cb97627afa1d2977b9f567f29598de / 11157405402824233090; o = 0\r\nGoogle-Actions-API-Version: 2\rXnX-SHARD:分片2\r\nVia:1.1 google\r\nAlt-Svc:clear\r\n\r\n {\ conversationToken\ \:\ [\\\ _actions_on_google\\\] \,\ expectUserResponse\:true,\ expectedInputs\:[{\ inputPrompt\:{},\ possibleIntents\:[{\ intent\:\ actions.intent.SIGN_IN\,\ inputValueData\:{\ @ type\:\ type.googleapis.com/google.actions.v2.SignInValueSpec\}}]}],\ responseMetadata\:{\ status\:{\ \ message\:\ Success(200)\},\ queryMatchInfo\:{\ queryMatched\:true,\ intent\:\ f645f492 -f6dc-4e7e-8da6-45711c654ad0\}},\ userStorage\:\ {\\\ data\\\:{}} \}。 ,
insertId: f9fzrtf3hjgn5,
资源:{
type: assistant_action,
labels:{
version_id :,
action_id: actions.intent.MAIN,
project_id: spee chbank-e8a15
}
},
timestamp: 2019-02-21T13:47:57.190979036Z,
severity: DEBUG,
labels:{
source: AOG_REQUEST_RESPONSE,
querystream: GOOGLE_USER,
channel:预览
},
LOGNAME: 项目/ speechbank-e8a15 /日志/ actions.googleapis.com%2Factions,
一丝: 项目/ 366800784520 /踪迹/ ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug,
receiveTimestamp: 2019-02-21T13:47:57.205496026Z
},
{
textPayload:带有帖子数据的发送请求:{\ user\:{\ userId\:\ ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg\,\ accessToken\:\ token1\,\ en-US\,\ lastSeen\:\ 2019-02-20T21:32:22Z\,\ userStorage\:\ {\\\ data\\\:{}} \},\ conversation\:{vers conversationId\:\ ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU8 1GCHbvlfZTxmULLd4Ug\,\ type\:\ ACTIVE\,\ conversationToken\:\ [\\\ _actions_on_google\\\] \ \},\输入\:[{\ intent\:\ actions.intent.SIGN_IN\,\ rawInputs\:[{}],\ arguments\:[{\\name\:\ SIGN_IN\,\扩展\:{\ @ type\:\ type.googleapis.com/ google.actions.v2.SignInValue\,\ status\:\ OK\}},{\ name\:\ text\}]}]], \ surface\:{\ capability\​​\:[{\ name\:\ actions.capability.WEB_BROWSER\},{\ name\:\ \ actions.capability.SCREEN_OUTPUT\},{\ name\:\ actions.capability.MEDIA_RESPONSE_AUDIO\},{\ name\:\ actions.capability。 AUDIO_OUTPUT\}]},\ isInSandbox\:true,\ availableSurfaces\:[{\capabilities\:[{\ name\:\ actions .capability.WEB_BROWSER\},{\ name\:\ actions.capability.SCREEN_OUTPUT\},{\ name\:\ actions.capability.AUDIO_OUTPUT\ }]}]}。,
insertId: 120k9w1f3jmw55,
资源:{
type: assistant_action,
labels: {
version_id:,
action_id: actions.intent.SIGN_IN,
project_id: speechbank-e8a15
}
},
timestamp: 2019-02 -21T13:48:28.768213970Z,
严重性: DEBUG,
标签:{
源: AOG_REQUEST_RESPONSE,
查询流: GOOGLE_USER,
频道:预览
},
logName: projects / speechbank-e8a15 / logs / actions.googleapis.com%2Factions,
trace: projects / 366800784520 / traces / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug,




























$ b $ 81 textPayload:已收到来自主体的代理程序的响应:HTTP / 1.1 200 OK服务器:nginx / 1.13.6服务器日期:2019年2月21日,星期四13:48:28 GMT ContentnContent-Type:application / json; charset = UTF-8\r\nContent-Length:570\r\nX-Cloud-Trace-Context:664d8fdaf9cd3d880d41f11ac2176e0e / 16724608154084655134; o = 0\r\ nGoogle-Actions-API-Version:2个版本tant-Interaction-Error-Code:-1\r\nAssistant-Interaction-Error-Message:由于空语音响应而无法将Dialogflow响应解析为AppResponse:rnX-SHARD:shard-2\r VnVia:1.1 google\r\nAlt-Svc:clear\r\n\r\n {\n\ responseMetadata\:{\n\ status\ :{\n \ code\:10,\n mes消息\:\由于语音响应为空,无法将Dialogflow响应解析为AppResponse, n\ \ details\:[{\n \ @ type\:\ type.googleapis.com/google.protobuf.Value\,\n \ value\: \ {\\\ id\\\:\\\ 5d4bed8d-c58c-4429-9838-f758d6f335f2\\\,\\\ timestamp\\\:\\\ 2019-02-21T13:48:28.806Z\\\,\\\ lang\\\ :\\\ zh-cn\\\,\\\结果\\\:{},\\\状态\\ \:{\\\ code\\\:200,\\\ errorType\\\:\\\成功\\ \\},\\\ sessionId\\\:\\\ ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug\\\} \ \n}] \n} \n} \n}。,
insertId: 120k9w1f3jmw56,
资源:{
type: assistant_action,
labels:{
project_id: speechbank-e8a15,
version_id:,
action_id: actions.intent.SIGN_IN
}
},
时间戳 : 2019-02-21T13:48:28.899033790Z,
severity: DEBUG,
labels:{
channel:预览,
源: AOG_REQUEST_RESPONSE,
querystream: GOOGLE_USER
},
logName: projects / speechbank-e8a15 / logs / actions.googleapis.com%2Factions ,
trace: projects / 366800784520 / traces / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug:
amp912,
amp912,
amp912,
amp912::
amp91::$ bb {
textPayload: MalformedResponse:由于语音响应空而无法将Dialogflow响应解析为AppResponse,
insertId: 1b6j2e6f39jvuy,
资源:{
type: assistant_action,
labels:{
project_id: speechbank-e8a15,
version_id:,
action_id: actions.intent.SIGN_IN
}
},
timestamp: 2019-02-21T13:48:28.899403302Z,
severity: ERROR ,
labels:{
channel:预览,
源: JSON_RESPONSE_VALIDATION,
querystream: GOOGLE_USER
} ,
logName: projects / speechbank-e8a15 / logs / actions.googleapis.com%2Factions,
trace: projects / 366800784520 / traces / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbbZQQN4KHKBCHZJQKnT receiveTimestamp: 2019-02-21T13:48:28.914061262Z
}
]

根据上述设置说明,任何人都可以帮助我们找出导致 MalformedResponse 异常的原因以及需要进行哪些更改以消除它。



这种异常情况太晦涩,以至于引发了无数问题,使人无所适从,从何处着手。我在这里只列出一些,非常感谢您提供一些指导。




  • AoG中的意图名称和Dialogflow之间是否存在任何关联?他们应该遵循任何命名约定吗?


  • 能否将MalformedResponse解释为缺少响应的特定字段?由于Google选择公开不同消息格式(Dialogflow和AppResponse)之间转换的内部工作,因此Dialogflow响应中是否需要列出某些字段?


  • 这是否意味着即使在这种情况下正在传递的OAuth消息也需要包含某些语音?


  • 最初,从Dialogflow收到的 userId 似乎总是 null ,但查询文本似乎填充了 GOOGLE_ASSISTANT_WELCOME ,因此我们基于以下假设开始帐户链接流逻辑: c> null 。


  • 在哪种情况下会首先填充 userId (例如在Alexa中启用了用户技能后会自动生成),以便上面的 else 条件可以被触发?


  • 由身份验证基础结构发行并由AoG支持的OAuth令牌应采用任何特定格式,即OIDC或JWT。可以是任何随机字符串吗?在AoG中, token1 是否仍然是有效的令牌(就像在Alexa中一样)?


  • 任何配置错误的令牌Java意图处理程序?我们应该对AoG帐户关联流程的响应中的哪个意图名称做出反应?


  • 是否有一个包罗万象的意图名称,可以将其处理程序合并到Java应用程序中,以方便进一步调试上述内容? / p>


  • 空语音响应是什么意思,我们没有提供预期会导致中断的值吗?


  • 我们已经配置的任何配置都不应该配置吗?




如果根本不重要,这是来自我们的网络挂钩的日志:

  [java] 2019年2月21日13:47:57 [qtp2056234595 -127] INFO domain.lola.user.utils.http.ControllerUtils [toString:30]-收到的AoG请求{
[java] responseId: 0156911c-d7e8-405b-bf8f-f23320c02030,
[java] queryResult:{
[java] queryText: GOOGLE_ASSISTANT_WELCOME,
[java] parameters:{
[java] any:
[java]},
[java] allRequiredParamsPresent:true,
[java] fulfillmentMessages:[{
[java ] text:{
[java] text:[]
[java]}
[java]}],
[java] outputContexts: [{
[java] name: projects / speechbank-e8a15 / agent / sessions / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmant [java $ ass any.original:,
[java] any:
[java]}
[java]},{
[java] name : projects / speechbank-e8a15 / agent / sessions / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug / contexts_b $ $ b _b b [java] any:
[java]}
[java]},{
[java] name: projects / speechbank-e8a15 / agent / sessions / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug / contexts / actions_cap skill_audio_output,
[java] parameters:{
[java] any.original:,
[java] any:
[java ]}
[java]},{
[java] name: projects / speechbank-e8a15 / agent / sessions / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvovoTTC_bcant_bs_bc_ant_bvb_b_b_b_bfc_b_b_b_c_b_b_c_b_b_c_b_b_c_a :{
[java] any.original:,
[java] any:
[java]}
[java]},{
[java]名称: projects / speechbank-e8a15 / agent / sessions / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7UUGCHbvlfZTxmcond [$ b] .original:,
[java] any:
[java]}
[java]},{
[java] name:项目/ speechbank-e8a15 / agent / sessions / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJ TkVh7yU81GCHbvlfZTxmULLd4Ug / contexts / actions_capability_media_response_audio,
[java] parameters:{
[java] any.original:,
[java] any:
[java]}
[java]}],
[java] intent:{
[java] name: projects / speechbank-e8a15 / agent / intents / f645f492-f6dc-4e7e-8da6-45711c654ad0,
[java] displayName: RawText
[java]},
[java] intentDetectionConfidence:1.0,
[java] languageCode: zh-cn
[java]},
[java] originalDetectIntentRequest:{
[java] source: google,
[java] version: 2,
[java] payload:{
[java] isInSandbox:true,
[java] surface: {
[java] capabilities:[{
[java] name: actions.capability.AUDIO_OUTPUT
[java]},{
[java]名称: actions.capability.MEDIA_RESPONSE_AUDIO
[java]},{
[java] name: actions.capability.SCREEN_OUTPUT
[java]},{
[java] name: actions.capability.WEB_BROWSER
[java]}]
[java]},
[java] inputs:[{
[java] rawInputs:[{
[java] query:开放语音库,
[java] inputType: VOICE
[java]}],
[java] intent: actions.intent.MAIN
[java]} ],
[java] user:{
[java] userStorage: {\ data\:{}},
[java] lastSeen : 2019-02-20T21:32:22Z,
[java] locale: zh-CN,
[java] userId: ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJJrGB- X96d9XY8k9XTJj-RUg9WpzGB9jg
[java]},
[java]对话:{
[java] conversationId: ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZQQ4KHbBZQQK : NEW
[java]} ,
[java] availableSurfaces:[{
[java] capabilities:[{
[java] name: actions.capability.AUDIO_OUTPUT
[ java]},{
[java] name: actions.capability.SCREEN_OUTPUT
[java]},{
[java] name: actions.capability.WEB_BROWSER
[java]}]
[java]}]
[java]}
[java]},
[java] session:项目/ speechbank-e8a15 / agent / sessions / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug
[java]}
[java5-1] t.tb.tk.tk.tk.tk.tn.google.com.tw..google.com.tw..google.com.tw.google.com.tw.google.com.tw.google.tw.google.tw.google.com.hk的开头是以下网址:[java]}。$。 .AoGApp [launchRequestHandler:26]-userId = null
[java] 2019年2月21日13:47:57 [qtp2056234595-127] INFO domain.lola.user.utils.actionsongoogle.AoGApp [launchRequestHandler:28] -queryText = GOOGLE_ASSISTANT_WELCOME
[java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO domain.lola.user.utils.actionsongoog le.AoGBotService [handlePOST:103]-生成的响应:
[java] {
[java] outputContexts:[{
[java] lifespanCount:99,
[java] name: projects / speechbank-e8a15 / agent / sessions / ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug / con [$] }
[java]}
[java]}],
[java]有效载荷:{
[java] google:{
[java ] expectUserResponse:true,
[java] isSsml:false,
[java] systemIntent:{
[java] intent: actions.intent.SIGN_IN ,
[java] data:{
[java] @type: type.googleapis.com/google.actions.v2.SignInValueSpec
[java]}
[java]},
[java] userStorage: {\ data\:{}}
[java]}
[java]}
[java]}


解决方案

此行

  responseBuilder.add(new SignIn()。setContext(speech)); 

将使用SIGN_IN事件创建响应。
因此,在您的dialogFlow中,您需要添加带有 actions_intent_SIGN_IN 的另一个意图,
,在您的Java中,您还需要实现


We are implementing Actions on Google with Dialogflow fulfillment using the newly released Java/Kotlin API.

It's called Speech Bank.

While going through the Account Linking process testing on the smartphone, the user is getting MalformedResponse error preventing the completion of the flow and consequent successful hand-off back to the regular flow.

The logs (detailed below) contain the MalformedResponse: Failed to parse Dialogflow response into AppResponse because of empty speech response message, and the user receives Speech bank isn't responding right now. Try again soon. message on her device.

Here's a bit more details on our setup:

The action is configured for Account Linking utilizing our own OAuth 2 compliant mock infrastructure.

There's a single intent (called RawText) configured in Dialogflow, the rest of the interactions are to be taken care of by own internal application via its web hook.

Here's how the state machine is coded in Java so far:

    public class AoGApp extends DialogflowApp {

        private final static Logger log = LoggerFactory.getLogger(AoGApp.class);

       public static final String GREETING = "GOOGLE_ASSISTANT_WELCOME";

        @ForIntent("RawText")
        //@ForIntent("actions.intent.MAIN")
        public ActionResponse launchRequestHandler(ActionRequest request) {

            String userId = request.getAppRequest().getUser().getUserId();
            log.info("userId={}",userId);
            String queryText = request.getWebhookRequest().getQueryResult().getQueryText();
            log.info("queryText={}", queryText);


            String speech = null;

            ResponseBuilder responseBuilder = getResponseBuilder(request);

            if (isBlank(userId) || GREETING.equalsIgnoreCase(queryText)) {

                speech = "\nHi. I sense a great banking experience in your future, I see that your account isn't connected. "
                        + "I've sent a link to your Google Assistant app that will get you started and set up in just several simple steps. "
                        + "Don't worry, I'll be here waiting, just summon me when you're ready.";


                responseBuilder.add(
                        new SignIn()
                            .setContext(speech));
            } else {
                speech = "Welcome. You can say hello.";
                responseBuilder.add(speech);
            }


            return responseBuilder.build();
        }

        @ForIntent("actions.intent.SIGN_IN")
        public ActionResponse getSignInStatus(ActionRequest request) {
          ResponseBuilder responseBuilder = getResponseBuilder(request);
          String text = "Hello from sign-in handler";
          responseBuilder.add(text);
          log.info(text);
          return responseBuilder.build();

        }

    }


and the associated HttpRequest processing: 

    @Override
        protected void handlePOST(final Request request, final HttpServletResponse response) {

            try {
                String rawRequest = ControllerUtils.toString(request.getReader());


                String jsonResponse = app.handleRequest(rawRequest, getHeadersMap(request)).get();
                log.info("Generated response:\n {}", ControllerUtils.prettyPrint(jsonResponse));
                response.setContentType(APPLICATION_JSON.getMimeType());
                response.getWriter().write(jsonResponse);
            } catch (Exception e) {
                handleError(response, e);
            } 
        }


public final class ControllerUtils {

    private final static Logger log = LoggerFactory.getLogger(ControllerUtils.class);
    private static ObjectMapper mapper = new ObjectMapper();

    public static String toString(BufferedReader reader) throws Exception {

        String rawRequest = reader
                .lines()
                //.map(e -> e.concat(System.lineSeparator()))
                .collect(Collectors.joining(System.lineSeparator()));
        log.info("Received AoG Request {}",rawRequest);
        return rawRequest;
    }

    public static String prettyPrint(String json) throws Exception {
        return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.readValue(json, Object.class));
    }

    public static Map<String, String> getHeadersMap(org.eclipse.jetty.server.Request jettyRequest){
        return Collections.list((Enumeration<String>) jettyRequest.getHeaderNames())
                .stream()
                .collect(Collectors.toMap(
                        name -> name,
                        jettyRequest::getHeader));

    }


}

As configured above, the OAuth authorization code flow undertakes the normal OAuth 2 steps:

  • hits /login endpoint to supply credentials

  • hits /token endpoint to obtain the token (its value is token1 in the below logs. We have a facility to generate & inject our own tokens, this is a testing environment so we produced this token1 value which seem to have been successfully incorporated into the subsequent request.)

Below is the detailed screen shot of failed interaction, with the attached log provided by Actions on Google console:

[
 {
   "textPayload": "Sending request with post data: {\"user\":{\"userId\":\"ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg\",\"locale\":\"en-US\",\"lastSeen\":\"2019-02-20T21:32:22Z\",\"userStorage\":\"{\\\"data\\\":{}}\"},\"conversation\":{\"conversationId\":\"ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug\",\"type\":\"NEW\"},\"inputs\":[{\"intent\":\"actions.intent.MAIN\",\"rawInputs\":[{\"inputType\":\"VOICE\",\"query\":\"open speech Bank\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.WEB_BROWSER\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.WEB_BROWSER\"}]}]}.",
   "insertId": "f9fzrtf3hjgn4",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "project_id": "speechbank-e8a15",
       "version_id": "",
       "action_id": "actions.intent.MAIN"
     }
   },
   "timestamp": "2019-02-21T13:47:56.713587946Z",
   "severity": "DEBUG",
   "labels": {
     "channel": "preview",
     "source": "AOG_REQUEST_RESPONSE",
     "querystream": "GOOGLE_USER"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:47:57.205496026Z"
 },
 {
   "textPayload": "Received response from agent with body: HTTP/1.1 200 OK\r\nServer: nginx/1.13.6\r\nDate: Thu, 21 Feb 2019 13:47:57 GMT\r\nContent-Type: application/json;charset=UTF-8\r\nContent-Length: 426\r\nX-Cloud-Trace-Context: d8cb97627afa1d2977b9f567f29598de/11157405402824233090;o=0\r\nGoogle-Actions-API-Version: 2\r\nX-SHARD: shard-2\r\nVia: 1.1 google\r\nAlt-Svc: clear\r\n\r\n{\"conversationToken\":\"[\\\"_actions_on_google\\\"]\",\"expectUserResponse\":true,\"expectedInputs\":[{\"inputPrompt\":{},\"possibleIntents\":[{\"intent\":\"actions.intent.SIGN_IN\",\"inputValueData\":{\"@type\":\"type.googleapis.com/google.actions.v2.SignInValueSpec\"}}]}],\"responseMetadata\":{\"status\":{\"message\":\"Success (200)\"},\"queryMatchInfo\":{\"queryMatched\":true,\"intent\":\"f645f492-f6dc-4e7e-8da6-45711c654ad0\"}},\"userStorage\":\"{\\\"data\\\":{}}\"}.",
   "insertId": "f9fzrtf3hjgn5",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "version_id": "",
       "action_id": "actions.intent.MAIN",
       "project_id": "speechbank-e8a15"
     }
   },
   "timestamp": "2019-02-21T13:47:57.190979036Z",
   "severity": "DEBUG",
   "labels": {
     "source": "AOG_REQUEST_RESPONSE",
     "querystream": "GOOGLE_USER",
     "channel": "preview"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:47:57.205496026Z"
 },
 {
   "textPayload": "Sending request with post data: {\"user\":{\"userId\":\"ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg\",\"accessToken\":\"token1\",\"locale\":\"en-US\",\"lastSeen\":\"2019-02-20T21:32:22Z\",\"userStorage\":\"{\\\"data\\\":{}}\"},\"conversation\":{\"conversationId\":\"ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug\",\"type\":\"ACTIVE\",\"conversationToken\":\"[\\\"_actions_on_google\\\"]\"},\"inputs\":[{\"intent\":\"actions.intent.SIGN_IN\",\"rawInputs\":[{}],\"arguments\":[{\"name\":\"SIGN_IN\",\"extension\":{\"@type\":\"type.googleapis.com/google.actions.v2.SignInValue\",\"status\":\"OK\"}},{\"name\":\"text\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]},\"isInSandbox\":true,\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]}]}.",
   "insertId": "120k9w1f3jmw55",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "version_id": "",
       "action_id": "actions.intent.SIGN_IN",
       "project_id": "speechbank-e8a15"
     }
   },
   "timestamp": "2019-02-21T13:48:28.768213970Z",
   "severity": "DEBUG",
   "labels": {
     "source": "AOG_REQUEST_RESPONSE",
     "querystream": "GOOGLE_USER",
     "channel": "preview"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:48:28.912828815Z"
 },
 {
   "textPayload": "Received response from agent with body: HTTP/1.1 200 OK\r\nServer: nginx/1.13.6\r\nDate: Thu, 21 Feb 2019 13:48:28 GMT\r\nContent-Type: application/json;charset=UTF-8\r\nContent-Length: 570\r\nX-Cloud-Trace-Context: 664d8fdaf9cd3d880d41f11ac2176e0e/16724608154084655134;o=0\r\nGoogle-Actions-API-Version: 2\r\nAssistant-Interaction-Error-Code: -1\r\nAssistant-Interaction-Error-Message: Failed to parse Dialogflow response into AppResponse because of empty speech response\r\nX-SHARD: shard-2\r\nVia: 1.1 google\r\nAlt-Svc: clear\r\n\r\n{\n  \"responseMetadata\": {\n    \"status\": {\n      \"code\": 10,\n      \"message\": \"Failed to parse Dialogflow response into AppResponse because of empty speech response\",\n      \"details\": [{\n        \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n        \"value\": \"{\\\"id\\\":\\\"5d4bed8d-c58c-4429-9838-f758d6f335f2\\\",\\\"timestamp\\\":\\\"2019-02-21T13:48:28.806Z\\\",\\\"lang\\\":\\\"en-us\\\",\\\"result\\\":{},\\\"status\\\":{\\\"code\\\":200,\\\"errorType\\\":\\\"success\\\"},\\\"sessionId\\\":\\\"ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug\\\"}\"\n      }]\n    }\n  }\n}.",
   "insertId": "120k9w1f3jmw56",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "project_id": "speechbank-e8a15",
       "version_id": "",
       "action_id": "actions.intent.SIGN_IN"
     }
   },
   "timestamp": "2019-02-21T13:48:28.899033790Z",
   "severity": "DEBUG",
   "labels": {
     "channel": "preview",
     "source": "AOG_REQUEST_RESPONSE",
     "querystream": "GOOGLE_USER"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:48:28.912828815Z"
 },
 {
   "textPayload": "MalformedResponse: Failed to parse Dialogflow response into AppResponse because of empty speech response",
   "insertId": "1b6j2e6f39jvuy",
   "resource": {
     "type": "assistant_action",
     "labels": {
       "project_id": "speechbank-e8a15",
       "version_id": "",
       "action_id": "actions.intent.SIGN_IN"
     }
   },
   "timestamp": "2019-02-21T13:48:28.899403302Z",
   "severity": "ERROR",
   "labels": {
     "channel": "preview",
     "source": "JSON_RESPONSE_VALIDATION",
     "querystream": "GOOGLE_USER"
   },
   "logName": "projects/speechbank-e8a15/logs/actions.googleapis.com%2Factions",
   "trace": "projects/366800784520/traces/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
   "receiveTimestamp": "2019-02-21T13:48:28.914061262Z"
 }
]

Based on the above setup description, could anyone please give us a hand figuring out what's causing the MalformedResponse exception and what needs to change to eliminate it.

This exception is so obscure that it brings about a myriad questions and places one at one's wits end as to where to start approaching it. I'll list just a few here and would really appreciate some guidance.

  • Should there be any correlation between intent names in AoG and Dialogflow? Should they follow any naming convention? Could the cause of the error lie in their somehow being misnamed?

  • Can the MalformedResponse be interpreted as lack of a particular field on the response? Since Google chose to expose the inner workings of the conversion between different message formats (Dialogflow and AppResponse), is there a listing somewhere of what fields are required on a Dialogflow response?

  • Is this to imply that even the OAuth messages that are being passed around in this case need to contain some speech?

  • Initially, the userId received from Dialogflow seems to always be null but the query text seems to be populated with GOOGLE_ASSISTANT_WELCOME, so we are starting the account linking flow logic based on the assumption that it is null. Is that a right assumption to have?

  • Under which circumstances will the userId be initially populated (like in Alexa where it's autogenerated upon enabling a skill for the user) so that the else condition above could get triggered?

  • Should the OAuth token issued by the authentication infrastructure and supported by AoG be in any particular format, i.e. OIDC or JWT. Could it be any random string? Is token1 still a valid token in AoG parlance (as it is in Alexa)?

  • Any misconfigured Java intent handler(s)? Which intent name in the response from the AoG account linking flow should we be reacting to?

  • Is there a catch-all intent name(s), a handler for which can be incorporated into the Java app to facilitate further debugging of the above?

  • What is meant by "empty speech response", what values are we not providing that are expected and cause the breakage?

  • Anything that we have configured which should not have been configured?

If it matters at all, here's the log from our webhook:

     [java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO  domain.lola.user.utils.http.ControllerUtils [toString:30]    - Received AoG Request {
 [java]   "responseId": "0156911c-d7e8-405b-bf8f-f23320c02030",
 [java]   "queryResult": {
 [java]     "queryText": "GOOGLE_ASSISTANT_WELCOME",
 [java]     "parameters": {
 [java]       "any": ""
 [java]     },
 [java]     "allRequiredParamsPresent": true,
 [java]     "fulfillmentMessages": [{
 [java]       "text": {
 [java]         "text": [""]
 [java]       }
 [java]     }],
 [java]     "outputContexts": [{
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/google_assistant_welcome",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/actions_capability_screen_output",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/actions_capability_audio_output",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/google_assistant_input_type_voice",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/actions_capability_web_browser",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }, {
 [java]       "name": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/actions_capability_media_response_audio",
 [java]       "parameters": {
 [java]         "any.original": "",
 [java]         "any": ""
 [java]       }
 [java]     }],
 [java]     "intent": {
 [java]       "name": "projects/speechbank-e8a15/agent/intents/f645f492-f6dc-4e7e-8da6-45711c654ad0",
 [java]       "displayName": "RawText"
 [java]     },
 [java]     "intentDetectionConfidence": 1.0,
 [java]     "languageCode": "en-us"
 [java]   },
 [java]   "originalDetectIntentRequest": {
 [java]     "source": "google",
 [java]     "version": "2",
 [java]     "payload": {
 [java]       "isInSandbox": true,
 [java]       "surface": {
 [java]         "capabilities": [{
 [java]           "name": "actions.capability.AUDIO_OUTPUT"
 [java]         }, {
 [java]           "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
 [java]         }, {
 [java]           "name": "actions.capability.SCREEN_OUTPUT"
 [java]         }, {
 [java]           "name": "actions.capability.WEB_BROWSER"
 [java]         }]
 [java]       },
 [java]       "inputs": [{
 [java]         "rawInputs": [{
 [java]           "query": "open speech Bank",
 [java]           "inputType": "VOICE"
 [java]         }],
 [java]         "intent": "actions.intent.MAIN"
 [java]       }],
 [java]       "user": {
 [java]         "userStorage": "{\"data\":{}}",
 [java]         "lastSeen": "2019-02-20T21:32:22Z",
 [java]         "locale": "en-US",
 [java]         "userId": "ABwppHFQHUBr0RrWA_OuL-kK2sxTPUvQtL3D-x2Ydr-7uxLt9zzEFzJrGB-X96d9XY8k9XTJj-RUg9WpzGB9jg"
 [java]       },
 [java]       "conversation": {
 [java]         "conversationId": "ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug",
 [java]         "type": "NEW"
 [java]       },
 [java]       "availableSurfaces": [{
 [java]         "capabilities": [{
 [java]           "name": "actions.capability.AUDIO_OUTPUT"
 [java]         }, {
 [java]           "name": "actions.capability.SCREEN_OUTPUT"
 [java]         }, {
 [java]           "name": "actions.capability.WEB_BROWSER"
 [java]         }]
 [java]       }]
 [java]     }
 [java]   },
 [java]   "session": "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug"
 [java] }
 [java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO  domain.lola.user.utils.actionsongoogle.AoGApp [launchRequestHandler:26]    - userId=null
 [java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO  domain.lola.user.utils.actionsongoogle.AoGApp [launchRequestHandler:28]    - queryText=GOOGLE_ASSISTANT_WELCOME
 [java] 02-21-2019 13:47:57 [qtp2056234595-127] INFO  domain.lola.user.utils.actionsongoogle.AoGBotService [handlePOST:103]    - Generated response:
 [java]  {
 [java]   "outputContexts" : [ {
 [java]     "lifespanCount" : 99,
 [java]     "name" : "projects/speechbank-e8a15/agent/sessions/ABwppHE35s8T6qSdaaCMNiWuMdY7UsvQ3sHbLZJOQkVA4AFD2nhKuqTTvoJTkVh7yU81GCHbvlfZTxmULLd4Ug/contexts/_actions_on_google",
 [java]     "parameters" : {
 [java]       "data" : "{}"
 [java]     }
 [java]   } ],
 [java]   "payload" : {
 [java]     "google" : {
 [java]       "expectUserResponse" : true,
 [java]       "isSsml" : false,
 [java]       "systemIntent" : {
 [java]         "intent" : "actions.intent.SIGN_IN",
 [java]         "data" : {
 [java]           "@type" : "type.googleapis.com/google.actions.v2.SignInValueSpec"
 [java]         }
 [java]       },
 [java]       "userStorage" : "{\"data\":{}}"
 [java]     }
 [java]   }
 [java] }

解决方案

this line

responseBuilder.add(new SignIn().setContext(speech));

Will create your response with SIGN_IN event. So in you dialogFlow you need to add another intent with actions_intent_SIGN_IN, and in your Java you need to implement it also, here you can find more info.

example dialogFlow:

这篇关于“格式错误的响应:由于语音响应为空,无法将Dialogflow响应解析为AppResponse”。在OAuth帐户关联流程中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆