title: asp.net core signalr error during websocket handshake
body: <p>Sorry for my bad English. I'm trying connect to SignalR Hub from SPA-application (Vue.JS).</p>
  
  <p>Code from client-side:</p>
  
  <pre><code>this.hubConnection = new HubConnectionBuilder()
      .withUrl('http://localhost:*****/testhub')
      .configureLogging(LogLevel.Information)
      .build()
  </code></pre>
  
  <p>I'm getting an error: <em>Error: Failed to complete negotiation with the server: SyntaxError: Unexpected token &lt; in JSON at position 0</em></p>
  
  <p>Then I tried:</p>
  
  <pre><code>this.hubConnection = new HubConnectionBuilder()
      .withUrl('http://localhost:*****/testhub', {
        skipNegotiation: true,
        transport: HttpTransportType.WebSockets
      })
      .configureLogging(LogLevel.Information)
      .build()
  </code></pre>
  
  <p>Nothing was changed (But I get another error):</p>
  
  <p><em>WebSocket connection to 'ws://localhost:50523/testhub' failed: Error during WebSocket handshake: Unexpected response code: 200</em></p>
  
  <p>I checked UseWebsockets in Startup.cs. Also I tried to change SignalR version in client-side, but it can't to solve the problem. When I tried to connect SignalR in many-page applications, I don't see it. I have it at first time.</p>
  
  <p>What I doing wrong?</p>
  
  <p><strong>Startup.cs</strong></p>
  
  <pre><code>using Microsoft.AspNetCore.Builder;
  using Microsoft.AspNetCore.Hosting;
  using Microsoft.AspNetCore.Mvc;
  using Microsoft.AspNetCore.Http.Features;
  using Microsoft.AspNetCore.SpaServices.Webpack;
  using Microsoft.Extensions.Configuration;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.AspNetCore.WebSockets;
  using Microsoft.EntityFrameworkCore;
  using Microsoft.AspNetCore.Identity;
  using Microsoft.AspNetCore.Authentication.JwtBearer;
  using Microsoft.IdentityModel.Tokens;
  using Microsoft.AspNetCore.Authorization;
  using Newtonsoft.Json;
  using Vue2Spa.Models;
  using Vue2Spa.Controllers.App;
  using Vue2Spa.Hubs;
  
  namespace Vue2Spa
  {
  public class Startup
  {
      public Startup(IConfiguration configuration)
      {
          Configuration = configuration;
      }
  
      public IConfiguration Configuration { get; }
  
      // This method gets called by the runtime. Use this method to add services to the container.
      public void ConfigureServices(IServiceCollection services)
      {
          // Add framework services.
          services.AddMvc().AddJsonOptions(options =&gt; options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore)
              .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
          var connection = @"Server=.\SQLEXPRESS;Database=****;Trusted_Connection=True;";
          services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt; options.UseSqlServer(connection));
  
          services.AddIdentity&lt;Account, IdentityRole&gt;(options =&gt;
          options.SignIn.RequireConfirmedEmail = true)
          .AddDefaultTokenProviders()
          .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;();
  
          //JWT
          services.AddAuthentication(options =&gt;
          {
              options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
              options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
          })
          .AddJwtBearer(options =&gt;
              {
                  options.RequireHttpsMetadata = false;
                  options.TokenValidationParameters = new TokenValidationParameters
                  {
                      ValidateIssuer = true,
                      ValidIssuer = AuthOptions.ISSUER,
                      ValidateAudience = true,
                      ValidAudience = AuthOptions.AUDIENCE,
                      ValidateLifetime = true,
                      IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
                      ValidateIssuerSigningKey = true,
                  };
              })
  
  
          services.AddAuthorization(options =&gt;
          {
  
          });
          /*services.AddAuthorization(options =&gt;
          {
              options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
                  .RequireAuthenticatedUser()
                  .Build();
          });*/
  
          // Simple example with dependency injection for a data provider.
          services.AddSingleton&lt;Providers.IWeatherProvider, Providers.WeatherProviderFake&gt;();
  
          //File's parameters
          services.Configure&lt;FormOptions&gt;(o =&gt; {
              o.ValueLengthLimit = int.MaxValue;
              o.MultipartBodyLengthLimit = int.MaxValue;
              o.MemoryBufferThreshold = int.MaxValue;
          });
  
          services.AddSignalR();
  
      }
  
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
  
              // Webpack initialization with hot-reload.
              app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
              {
                  HotModuleReplacement = true,
              });
          }
          else
          {
              app.UseExceptionHandler("/Home/Error");
          }
  
          app.UseStaticFiles();
  
          app.UseMvc(routes =&gt;
          {
              routes.MapRoute(
                  name: "default",
                  template: "{controller=Home}/{action=Index}/{id?}");
  
              routes.MapRoute(
                  name: "default1",
                  template: "{controller=Admin}/{action=Index}/{id?}");
  
              routes.MapSpaFallbackRoute(
                  name: "spa-fallback",
                  defaults: new { controller = "Home", action = "Index" });
  
              routes.MapSpaFallbackRoute(
                  name: "spa-fallback1",
                  defaults: new { controller = "Admin", action = "Index" });
          });
          app.UseAuthentication();
          app.Use(async (context, next) =&gt;
          {
              context.Response.Headers.Add("X-Xss-Protection", "1");
              await next();
          }).UseSignalR(route =&gt;
          {
              route.MapHub&lt;ChatHub&gt;("/chat");
          });
      }
  }
  }
  </code></pre>