microsoft / botframework-sdk

Bot Framework provides the most comprehensive experience for building conversation applications.
MIT License
7.51k stars 2.45k forks source link

getting 405 . Web App Bot connected to local using ngrok. #5136

Closed Unders0n closed 6 years ago

Unders0n commented 6 years ago

env: net core 2.1, c# bot builder 4.1.1 net.core.app 2.1.5 bot enterprice template (w\o running of az scripts)

what's done: i've created a chatbot app using latest enterprise template. I haven't run azure resource script though 'cos of inner policy, so we just created resourses using regular bot service creation and configured manually. I commented some services trying to minimize them to minimal set of working. I've commented Transcript Middleware, i changed State service to use MemoryStorage:

            var dataStore = new MemoryStorage();
            var userState = new UserState(dataStore);
            var conversationState = new ConversationState(dataStore);

            services.AddSingleton(dataStore);
            services.AddSingleton(userState);
            services.AddSingleton(conversationState);

            services.AddSingleton(new BotStateSet(userState, conversationState));

Took all app ids, keys and what is needed, configured my app. So when im running locally and testing using emulator, everythings work fine. Now i'm trying to connect bot service to my local app using ngrok tunnel to debug it. I'm running my app on 32618 port. I've done basic forwarding using ngrok: ngrok.exe http 32618 -host-header="localhost:32618" and now its listening on https://deb52201.ngrok.io , i've updated .bot file to use "endpoint": "https://deb52201.ngrok.io/api/messages", in launchSetytings i'm still using "applicationUrl": "http://localhost:32618". Updated message endpoint in azure bot app to https://deb52201.ngrok.io/api/messages

problem: when i'm using emulator it's working (both with local and external ngrok urls) , though it gives me error :Error: The bot is remote, but the service URL is localhost. Without tunneling software you will not receive replies" (dunno what it means by service URL is localhost). But when i'm trying to test using azure's Test in web chat i see error that message can't be sent. In ngrok i see in log that bot service was trying to call OPTIONS /api/messages and result is "not allowed". In vs i see this error: Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 OPTIONS http://localhost:32618/api/messages
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 4.3045ms 405 Application Insights Telemetry:

{"name":"Microsoft.ApplicationInsights.Dev.43984f36641f4cf391ac866e42e51bd7.Request",
"time":"2018-11-06T10:12:43.6894710Z",
"iKey":"43984f36-641f-4cf3-91ac-866e42e51bd7",
"tags":{"ai.application.ver":"1.0.0.0",
"ai.cloud.roleInstance":"VzlomNote",
"ai.operation.id":"d7fbd988a140254b968de0ee7bbb6d28",
"ai.operation.name":"OPTIONS /api/messages",
"ai.location.ip":"192.40.95.29",
"ai.internal.sdkVersion":"aspnet5c:2.5.1",
"ai.internal.nodeName":"VZLOMNOTE"},
"data":{"baseType":"RequestData",
"baseData":{"ver":2,
"id":"|d7fbd988a140254b968de0ee7bbb6d28.9ade66b2_",
"name":"OPTIONS /api/messages",
"duration":"00:00:00.0099015",
"success":false,
"responseCode":"405",
"url":"http://localhost:32618/api/messages",
"properties":
{"_MS.ProcessedByMetricExtractors":"(Name:'Requests', Ver:'1.1')",
"DeveloperMode":"true",
"AspNetCoreEnvironment":"Local"}}}}

The thread 0xda4 has exited with code 0 (0x0).

When googling related problems i constantly see advice to change stateManager. but a) i have InMemory already (hope its configured right) 2)all examples are for old versions on web api, there's no such methods, i can't find any examples for Net Core and BorBuilder 4.1.

Full code: launchSettings:

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:32618",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "ChatbotCore": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Local"
      },
      "applicationUrl": "http://localhost:32618"
    }
  }
}

Startup:

 // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace AgriSync.ChatbotCore
{
    public class Startup
    {
        private bool _isProduction = false;

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();

            Configuration = builder.Build();
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // Load the connected services from .bot file.
            var botFilePath = Configuration.GetSection("botFilePath")?.Value;
            var botFileSecret = Configuration.GetSection("botFileSecret")?.Value;
            var botConfig = BotConfiguration.Load(botFilePath ?? @".\ChatbotCore.bot", botFileSecret);
            services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded."));

            // Initializes your bot service clients and adds a singleton that your Bot can access through dependency injection.
            var connectedServices = new BotServices(botConfig);
            services.AddSingleton(sp => connectedServices);

            // Initialize Bot State
            /*var cosmosDbService = botConfig.Services.FirstOrDefault(s => s.Type == ServiceTypes.CosmosDB) ?? throw new Exception("Please configure your CosmosDb service in your .bot file.");
            var cosmosDb = cosmosDbService as CosmosDbService;
            var cosmosOptions = new CosmosDbStorageOptions()
            {
                CosmosDBEndpoint = new Uri(cosmosDb.Endpoint),
                AuthKey = cosmosDb.Key,
                CollectionId = cosmosDb.Collection,
                DatabaseId = cosmosDb.Database,
            };*/
            // var dataStore = new CosmosDbStorage(cosmosOptions);
/*

            var cosmosDbService = botConfig.Services.FirstOrDefault(s => s.Type == ServiceTypes.CosmosDB) ?? throw new Exception("Please configure your CosmosDb service in your .bot file.");
            var cosmosDb = cosmosDbService as CosmosDbService;
            var cosmosOptions = new CosmosDbStorageOptions()
            {
                CosmosDBEndpoint = new Uri(cosmosDb.Endpoint),
                AuthKey = cosmosDb.Key,
                CollectionId = cosmosDb.Collection,
                DatabaseId = cosmosDb.Database,
            }; 
             var dataStore = new CosmosDbStorage(cosmosOptions);*/

             //user and conversation state

             var dataStore = new MemoryStorage();
            var userState = new UserState(dataStore);
            var conversationState = new ConversationState(dataStore);

            services.AddSingleton(dataStore);
            services.AddSingleton(userState);
            services.AddSingleton(conversationState);

            services.AddSingleton(new BotStateSet(userState, conversationState));

            services.AddMvc();

            // Add the bot with options
            services.AddBot<ChatbotCore>(options =>
            {
                // Load the connected services from .bot file.
                var environment = _isProduction ? "production" : "development";
                var service = botConfig.Services.FirstOrDefault(s => s.Type == ServiceTypes.Endpoint && s.Name == environment);
                if (!(service is EndpointService endpointService))
                {
                    throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
                }

                options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);

                // Telemetry Middleware (logs activity messages in Application Insights)
                var appInsightsService = botConfig.Services.FirstOrDefault(s => s.Type == ServiceTypes.AppInsights) ?? throw new Exception("Please configure your AppInsights connection in your .bot file.");
                var instrumentationKey = (appInsightsService as AppInsightsService).InstrumentationKey;
                var appInsightsLogger = new TelemetryLoggerMiddleware(instrumentationKey, logUserName: true, logOriginalMessage: true);
                options.Middleware.Add(appInsightsLogger);

                // Catches any errors that occur during a conversation turn and logs them to AppInsights.
                options.OnTurnError = async (context, exception) =>
                {
                    await context.SendActivityAsync("Sorry, it looks like something went wrong.");
                    connectedServices.TelemetryClient.TrackException(exception);
                };

                // Transcript Middleware (saves conversation history in a standard format)
                /*var storageService = botConfig.Services.FirstOrDefault(s => s.Type == ServiceTypes.BlobStorage) ?? throw new Exception("Please configure your Azure Storage service in your .bot file.");
                var blobStorage = storageService as BlobStorageService;
                var transcriptStore = new AzureBlobTranscriptStore(blobStorage.ConnectionString, blobStorage.Container);
                var transcriptMiddleware = new TranscriptLoggerMiddleware(transcriptStore);
                options.Middleware.Add(transcriptMiddleware);*/

                // Typing Middleware (automatically shows typing when the bot is responding/working)
                var typingMiddleware = new ShowTypingMiddleware();
                options.Middleware.Add(typingMiddleware);
                options.Middleware.Add(new AutoSaveStateMiddleware(userState, conversationState));

                /*IStorage dataStore = new MemoryStorage();

                var conversationState = new ConversationState(dataStore);
                options.State.Add(conversationState);

                var userState = new UserState(dataStore);
                options.State.Add(userState);*/

            });
        }

        /// <summary>
        /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        /// </summary>
        /// <param name="app">Application Builder.</param>
        /// <param name="env">Hosting Environment.</param>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            _isProduction = env.IsProduction();
            app.UseDefaultFiles()
                .UseStaticFiles()
                .UseBotFramework()
                .UseMvcWithDefaultRoute();
        }
    }
}

ChatbotLocal.bot:

{
    "name": "AgriSyncBot",
    "description": "",
    "services": [
      {
        "type": "endpoint",
        "appId": "<CODE>",
        "appPassword": "<CODE>",
        "endpoint": "https://a2d77e39.ngrok.io/api/messages",
        "id": "1",
        "name": "development"
      },
        {
            "type": "appInsights",
            "InstrumentationKey": "<CODE>",
            "apiKeys": {}
        }
    ],
    "padlock": "",
    "version": "2.0",
    "overrides": null,
    "path": "C:\\Users\\VzLOM\\Source\\Repos\\agrisync-v33\\ChatbotCore\\ChatbotCoreLocal.bot"
}

Thanks

EricDahlvang commented 6 years ago

Hi @Unders0n

Thank you for trying out the Enterprise Bot Template. The repository for issues related to this template is https://github.com/Microsoft/BotBuilder-samples/issues The BotBuilder repository issues is now for DCRs only. If you have further questions or issues related to the Enterprise Bot Template, please ask on Stack Overflow or BotBuilder-samples


1) The OPTIONS call is sent to the bot's messaging endpoint by the Azure Portal when you navigate to settings within the portal. Since only POSTs are handled by the bot's messaging endpoint, and not OPTIONS or GET or PUT, these other commands will result in 'not allowed'. You can add code to handle these call types, if you prefer. But, the failure of the OPTIONS call from the portal can be safely ignored.

2) "The bot is remote, but the service URL is localhost." means that ngrok is not properly configured for the emulator itself. Within the v4 Emulator, use the View -> Emulator Settings menus, and set the 'Path to ngrok':

image

The emulator repository has an open issue to make this wording more clear: https://github.com/Microsoft/BotFramework-Emulator/issues/878

Unders0n commented 6 years ago

@EricDahlvang Still my problem was not revealed in this answer, so i've created issue in repo you suggested: https://github.com/Microsoft/BotBuilder-Samples/issues/864