【问题标题】:How to handle DirectLine connection errors如何处理 DirectLine 连接错误
【发布时间】:2020-11-16 20:49:08
【问题描述】:

我们使用 bot 框架运行一个机器人,并使用 JS 中的直接线路从我们的网站连接到它。我们从自定义 API 端点获取令牌,并将令牌存储在 sessionStorage 中。然后我们使用

连接到机器人
directLine = await window.WebChat.createDirectLine({
  token,
  conversationId,
  watermark: "0"
});

一切正常,但是当我让页面打开太久时,sessionStorage 中的令牌会过期。页面刷新或导航到其他页面会导致 createDirectLine 方法内出现 403 错误。只要 sessionStorage 持有该令牌,就会导致聊天机器人无法连接。这种行为对我来说并不奇怪,但我不知道如何处理。

我想要的是简单地清除 sessionStorge,请求一个新的令牌并在发生这种情况时开始一个新的对话。但我不知道该怎么做。如何从 createDirectLine 方法中获取 403 错误?或者有没有办法预先验证令牌? 我已经尝试在 createDirectLine 方法周围放置一个 try/catch 块,但是 403 错误没有出现在 catch 中。

提前致谢!

【问题讨论】:

  • 您是否尝试在 sessionStorage 中存储带有令牌的时间戳?
  • 感谢您的回复。我还没有尝试过,有时间我会尝试的。这会起作用吗?我觉得时间戳可能会不同步。加上 DirectLine 会自动响应令牌(我认为),但我不确定它何时会这样做。您有为此目的使用此方法的经验吗?

标签: javascript botframework direct-line-botframework


【解决方案1】:

此解决方案仅用于解决令牌过期后发生的 403 错误(我认为在 30 分钟内)。更好的解决方案是将 conversationId 与令牌一起存储并获得一个新令牌。检查official bot service documentation

// to shorten code, we store in sessionStorage as separate items.
const expirationDuration = 1000 * 60 * 30; // 30 minutes
const currentTime = new Date().getTime();
const timeTokenStored = sessionStorage.getItem("timeTokenStored") || currentTime;

// if token is stored over 30 minutes ago, ignore it and get a new one. Otherwise, use it.
if ((currentTime - timeTokenStored) > expirationDuration) {
      const res = await fetch('https://<yourTokenEndpoint>', { method: 'POST' });
      const { token } = await res.json();}
      const currentTime = new Date().getTime();
      sessionStorage.setItem("timeTokenStored", currentTime);
      sessionStorage.setItem('token', token);
else {
  const token = sessionStorage.getItem("token")
}

当您使用它时,不妨将其存储在 localStorage 中。这样,您的机器人将跟随用户。

【讨论】:

  • 谢谢。这似乎有效。我仍然会对真正的后备功能感兴趣。在 window.WebChat.createDirectLine 方法的某个地方,错误被捕获。如果我们能看到这些错误并采取行动,那就太好了。
  • 这个解决方案在实践中证明不太理想,所以我们开发了自己的解决方案(见我的回答)。还是谢谢。
【解决方案2】:

我找到了解决方案。我们可以通过刷新来检查令牌是否有效。如果刷新导致错误,则令牌不再有效。如果刷新成功,令牌将再有效一个小时。

所以我们在后端添加(可重用)一个函数来使用https://directline.botframework.com/v3/directline/tokens/refresh 刷新令牌。 我们更改了前端代码以调用我们新的刷新函数。

前端代码:

// Gets a new token from the cloud.
async function requestToken() {
  if (!sessionStorage['webchatToken']) {
    const res = await fetch('https://' + serviceName + '.azurewebsites.net/api/token');
    // If the request was succesfull, store the token and userId.
    if (res.status == 200) {
      const jsonResult = await res.json();
      sessionStorage['webchatToken'] = jsonResult.token;
      sessionStorage['webchatUserId'] = jsonResult.userId;
      console.log(`Got token from cloud`);

      // refresh the token every 15 minutes.
      setTimeout(() => {
        refreshToken();
      }, 60000 * 15); // 15 minutes
    }
    // If the request was not succesfull, retry.
    else {
      console.log(`Tried to get token, but goterror ` + res.status + `. Retrying.`);
      await requestToken();
    }
  }
  // If there is already a token in storage, refresh the existing one instead of requesting a new one.
  else {
    console.log(`Got token from sessionStorage`);
    await refreshToken();
  }
}

// Refreshes an existing token so it doesn't expire.
async function refreshToken() {
  // Refresh the token if it exists in storage.
  if (sessionStorage['webchatToken']) {
    const res = await fetch('https://' + serviceName + '.azurewebsites.net/api/token/refresh?token=' + sessionStorage['webchatToken'],
    {
      method: 'POST'
    });
    // If refresh was succesfull we are done.
    if (res.status == 200) {
      console.log(`Refreshed token`);
    }
    // If refresh was not succesfull, clear the token from storage and request a new one. The token is probably expired.
    else {
      console.log(`Tried to refresh token, but got error ` + res.status + `. Requesting new token.`);
      sessionStorage.clear();
      await requestToken();
    }
  }
  // If there is no token in storage, request a new token.
  else {
    console.log(`Tried to refresh token, but token is not defined. Requesting new token.`);
    sessionStorage.clear();
    await requestToken();
  }
}

后端代码:

[HttpGet]
[Route("api/token")]
public async Task<ObjectResult> GetToken()
{
  HttpClient client = new HttpClient();

  HttpRequestMessage request = new HttpRequestMessage(
    HttpMethod.Post,
    $"https://directline.botframework.com/v3/directline/tokens/generate");
 
  request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _configuration.DirectLineKey);

  var userId = $"dl_{Guid.NewGuid()}";

  request.Content = new StringContent(
    JsonConvert.SerializeObject(new { User = new { Id = userId } }),
    Encoding.UTF8,
    "application/json");

  var response = await client.SendAsync(request);
  string token = String.Empty;
  int expiresIn = 0;

  if (response.IsSuccessStatusCode)
  {
    var body = await response.Content.ReadAsStringAsync();
    token = JsonConvert.DeserializeObject<DirectLineToken>(body).token;
    expiresIn = JsonConvert.DeserializeObject<DirectLineToken>(body).expires_in;
  }

  return Ok(new { token, userId, expiresIn });
}

[HttpPost]
[Route("api/token/refresh/")]
public async Task<ObjectResult> RefreshToken(string token)
{
  HttpClient client = new HttpClient();

  HttpRequestMessage request = new HttpRequestMessage(
    HttpMethod.Post,
    $"https://directline.botframework.com/v3/directline/tokens/refresh");

  request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);


  var response = await client.SendAsync(request);
  token = String.Empty;
  int expiresIn = 0;

  if (response.IsSuccessStatusCode)
  {
    var body = await response.Content.ReadAsStringAsync();
    token = JsonConvert.DeserializeObject<DirectLineToken>(body).token;
    expiresIn = JsonConvert.DeserializeObject<DirectLineToken>(body).expires_in;
  }

  if (string.IsNullOrEmpty(token))
    return Problem("Token incorrect");

  return Ok(new { token, expiresIn });
}

我希望发布此内容可能对某人有用。

【讨论】:

    猜你喜欢
    • 2020-02-21
    • 2021-07-30
    • 1970-01-01
    • 2022-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-11
    • 2012-05-01
    相关资源
    最近更新 更多