如何在 IIS 上托管 BOT Framework V4 BOT

How to host BOT Framework V4 BOT on IIS

我在 IIS 上托管我的 BOT Framework V4 BOT。 服务器配置如下

  1. Windows 服务器 2012 R2
  2. IIS 8.5

我还安装了 dotnet-hosting-2.2.1-win.exe,这是托管 BOT V4 所必需的。我已将应用程序池设置为无托管代码。 我已经设置了 Startup.cs 文件所在文件夹的路径。 另请查找 BOT 生成的日志附件。 仍然,它不工作错误如下

HTTP 错误 403.14 - 禁止访问 Web 服务器配置为不列出此目录的内容。

Detailed Error Information:
Module     DirectoryListingModule
Notification       ExecuteRequestHandler
Handler    StaticFile
Error Code     0x00000000
Requested URL      https://myServer/ConfRoomBOT/
Physical Path      ProjectPath
Logon Method       Anonymous
Logon User     Anonymous
Request Tracing Directory      C:\inetpub\logs\FailedReqLogFiles

下面是web.config代码

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\ConfRoom.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" />
    </system.webServer>
  </location>
  <system.net>
     <defaultProxy useDefaultCredentials="false">
        <proxy usesystemdefault="False" proxyaddress="http://xx.xxx.xx.xxx:8080" bypassonlocal="True" />
      </defaultProxy>
  </system.net>
</configuration>

以下是BOT生成的日志

Hosting environment: Production Content root path: E:\Project\DotNetCoreApps\ConfRoomPub\WithPII\publish Now listening on: http://127.0.0.1:41826 Application started. Press Ctrl+C to shut down. info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://example.com/ConfRoom/ info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://example.com/ConfRoom/ info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] Sending file. Request path: '/default.htm'. Physical path: 'E:\Project\DotNetCoreApps\ConfRoomPub\WithPII\publish\wwwroot\default.htm' info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[6] The file /default.htm was not modified info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 184.4402ms 304 text/html info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 188.1919ms 200 text/html info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 POST http://example.com/ConfRoom/api/messages application/json; charset=utf-8 373 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 POST http://example.com/ConfRoom/api/messages application/json; charset=utf-8 359 fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HLJNNKCOIPS7", Request id "0HLJNNKCOIPS7:00000002": An unhandled exception was thrown by the application. System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://login.botframework.com/v1/.well-known/openidconfiguration'. ---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'https://login.botframework.com/v1/.well-known/openidconfiguration'. ---> System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken) at System.Threading.Tasks.ValueTask1.get_Result() at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Threading.Tasks.ValueTask1.get_Result() at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask1 creationTask) at System.Threading.Tasks.ValueTask1.get_Result()
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(String address, IDocumentRetriever retriever, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel) at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync() at Microsoft.Bot.Connector.Authentication.JwtTokenExtractor.ValidateTokenAsync(String jwtToken, String channelId) at Microsoft.Bot.Connector.Authentication.JwtTokenExtractor.GetIdentityAsync(String scheme, String parameter, String channelId) at Microsoft.Bot.Connector.Authentication.JwtTokenExtractor.GetIdentityAsync(String authorizationHeader, String channelId) at Microsoft.Bot.Connector.Authentication.ChannelValidation.AuthenticateChannelToken(String authHeader, ICredentialProvider credentials, HttpClient httpClient, String channelId) at Microsoft.Bot.Connector.Authentication.ChannelValidation.AuthenticateChannelToken(String authHeader, ICredentialProvider credentials, String serviceUrl, HttpClient httpClient, String channelId) at Microsoft.Bot.Connector.Authentication.JwtTokenValidation.ValidateAuthHeader(String authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, String channelId, String serviceUrl, HttpClient httpClient) at Microsoft.Bot.Connector.Authentication.JwtTokenValidation.AuthenticateRequest(IActivity activity, String authHeader, ICredentialProvider credentials, IChannelProvider provider, HttpClient httpClient) at Microsoft.Bot.Builder.BotFrameworkAdapter.ProcessActivityAsync(String authHeader, Activity activity, BotCallbackHandler callback, CancellationToken cancellationToken) at Microsoft.Bot.Builder.Integration.AspNet.Core.Handlers.BotMessageHandler.ProcessMessageRequestAsync(HttpRequest request, IAdapterIntegration adapter, BotCallbackHandler botCallbackHandler, CancellationToken cancellationToken) at Microsoft.Bot.Builder.Integration.AspNet.Core.Handlers.BotMessageHandlerBase.HandleAsync(HttpContext httpContext) at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Builder.Extensions.UsePathBaseMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication1 application) fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HLJNNKCOIPS6", Request id "0HLJNNKCOIPS6:00000002": An unhandled exception was thrown by the application. System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://login.botframework.com/v1/.well-known/openidconfiguration'.<br> at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync(CancellationToken cancel) at Microsoft.IdentityModel.Protocols.ConfigurationManager1.GetConfigurationAsync() at Microsoft.Bot.Connector.Authentication.JwtTokenExtractor.ValidateTokenAsync(String jwtToken, String channelId) at Microsoft.Bot.Connector.Authentication.JwtTokenExtractor.GetIdentityAsync(String scheme, String parameter, String channelId) at Microsoft.Bot.Connector.Authentication.JwtTokenExtractor.GetIdentityAsync(String authorizationHeader, String channelId) at Microsoft.Bot.Connector.Authentication.ChannelValidation.AuthenticateChannelToken(String authHeader, ICredentialProvider credentials, HttpClient httpClient, String channelId) at Microsoft.Bot.Connector.Authentication.ChannelValidation.AuthenticateChannelToken(String authHeader, ICredentialProvider credentials, String serviceUrl, HttpClient httpClient, String channelId) at Microsoft.Bot.Connector.Authentication.JwtTokenValidation.ValidateAuthHeader(String authHeader, ICredentialProvider credentials, IChannelProvider channelProvider, String channelId, String serviceUrl, HttpClient httpClient) at Microsoft.Bot.Connector.Authentication.JwtTokenValidation.AuthenticateRequest(IActivity activity, String authHeader, ICredentialProvider credentials, IChannelProvider provider, HttpClient httpClient) at Microsoft.Bot.Builder.BotFrameworkAdapter.ProcessActivityAsync(String authHeader, Activity activity, BotCallbackHandler callback, CancellationToken cancellationToken) at Microsoft.Bot.Builder.Integration.AspNet.Core.Handlers.BotMessageHandler.ProcessMessageRequestAsync(HttpRequest request, IAdapterIntegration adapter, BotCallbackHandler botCallbackHandler, CancellationToken cancellationToken) at Microsoft.Bot.Builder.Integration.AspNet.Core.Handlers.BotMessageHandlerBase.HandleAsync(HttpContext httpContext) at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Builder.Extensions.UsePathBaseMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication1 application) info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 21679.9889ms 500 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 14958.2414ms 500 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 POST http://127.0.0.1:41826/CONFROOM/iisintegration 0 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 0.373ms 202 Application is shutting down...

请帮忙。 谢谢。

我认为 Drew Marsh 在他的回复中发现了问题,这可能是一个 web.config 问题。

如果您要在 IIS 中托管 .net 核心应用程序,IIS 仍然需要在应用程序目录中有一个 web.config,它至少有一个处理程序映射和一个 aspNetCore 路径。

基本上,IIS 必须知道该应用程序是一个 .net 核心应用程序以及 运行 的代码。

我可能是错的,但这看起来像对待静态文件一样处理所有内容。

I get this error if I remove the web.config from a working .net core app hosted in IIS.

看起来和你的错误几乎一模一样,我也犯了类似的错误。

我通常很幸运地将我的项目发布为 IIS Web 部署包。这往往会创建所有必要的位。

工作的 web.confg 可能看起来像这样。

<configuration>
      <system.webServer>
        <handlers>
          <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
        </handlers>
        <aspNetCore processPath="dotnet" arguments=".\blagh.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
      </system.webServer>
</configuration>

根据您添加到问题中的异常详细信息,这是根本问题:

An unhandled exception was thrown by the application. System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://login.botframework.com/v1/.well-known/openidconfiguration'. ---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'https://login.botframework.com/v1/.well-known/openidconfiguration'. ---> System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

现在,如果您使用浏览器导航至该页面,您应该会看到获得包含描述 OpenID 配置的 JSON 元数据文档的响应没有问题:

{
  "issuer": "https://api.botframework.com",
  "authorization_endpoint": "https://invalid.botframework.com",
  "jwks_uri": "https://login.botframework.com/v1/.well-known/keys",
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "token_endpoint_auth_methods_supported": [
    "private_key_jwt"
  ]
}

好消息是您的机器人似乎运行良好。这实际上表明机器人 运行 已经足够远,它正在验证请求,所以这是一个好兆头。这个异常对我来说是什么,无论你的机器人在哪里托管,都可能无法向互联网发出出站请求。那可能吗?它需要能够验证来自 bot 框架的 JWT 令牌的真实性,并最终需要它通过 bot 框架将响应发送回客户端。您可能需要将对 botframework.com 域的 HTTP 请求列入白名单。

这是 IIS 的身份验证问题。我们必须使用具有所有权限的用户帐户,即管理员来配置以下内容

  1. 应用程序池标识
  2. 托管应用程序的 IIS 身份验证和授权(连接为)。

我删除了我为身份验证编写的所有代码,如 OAuth 等

感谢大家抽出时间。