由于CORS问题,无法让SignalR正常工作 - 受到CORS策略的阻止。

4

我正在使用React与Signal R。

我有一个托管我的hub的标准Web应用程序。

当我发送消息时,在Web应用程序的网页上所有功能都可以完美运行。

我还有一个托管在端口3000上的React应用程序。

我按照以下方式更改了IIS Express设置。

    <httpProtocol>
      <customHeaders>
        <clear />
        <add name="X-Powered-By" value="ASP.NET" />
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
      </customHeaders>
      <redirectHeaders>
        <clear />
      </redirectHeaders>
    </httpProtocol>

我的服务器端启动cors等代码如下:

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddCors(options =>
        {
            options.AddPolicy("cors",
                builder =>
                {
                    builder
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .WithOrigins("http://localhost:3000");
                });
        });

        services.AddSignalR();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseCors("cors");
        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<ChatHub>("/chatHub");
            endpoints.MapRazorPages();
        });
    }

在 React 方面,我已经实现了以下内容

import React, { Component } from 'react';
import * as signalR from '@aspnet/signalr';

class Chat extends Component {
  constructor(props) {
    super(props);

    this.state = {
      nick: '',
      message: '',
      messages: [],
      hubConnection: null,
    };
  }

  componentDidMount = () => {
    const protocol = new signalR.JsonHubProtocol();
    const transport = signalR.HttpTransportType.WebSockets;

    const options = {
      transport,
      logMessageContent: true,
      logger: signalR.LogLevel.Trace,
    };

    // create the connection instance
    var hubConnection = new signalR.HubConnectionBuilder()
      .withUrl("http://localhost:44360/chatHub", options)
      .withHubProtocol(protocol)
      .build();

    this.setState({ hubConnection }, () => {
      this.state.hubConnection
        .start()
        .then(() => console.log('Connection started!'))
        .catch(err => console.log('Error while establishing connection :('));

      this.state.hubConnection.on('SendMessage', (user, message) => {
        const text = `${user}: ${message}`;
        const messages = this.state.messages.concat([text]);

        console.log('ssss');

        this.setState({ messages });
      });
    });
  }

  render() {
    return (
      <div>
        <br />

        <div>
          {this.state.messages.map((message, index) => (
            <span style={{display: 'block'}} key={index}> {message} </span>
          ))}
        </div>
      </div>
    );
  }
}

export default Chat;

您可以看到,我已连接到我的服务器应用程序所在的确切端口

日志中显示我已连接成功

但实际上我没有收到任何消息?

下面是我的Web应用程序中的hub:

"use strict";

var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;

connection.on("ReceiveMessage", function (user, message) {
    var msg = message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    var encodedMsg = user + " says " + msg;
    var li = document.createElement("li");
    li.textContent = encodedMsg;
    document.getElementById("messagesList").appendChild(li);
});

connection.start().then(function () {
    document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
    return console.error(err.toString());
});

document.getElementById("sendButton").addEventListener("click", function (event) {
    var user = document.getElementById("userInput").value;
    var message = document.getElementById("messageInput").value;
    connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString());
    });
    event.preventDefault();
});

我曾以为我解决了Cors的问题,但当我将网页保持打开一段时间后,我收到了以下错误提示:
Access to XMLHttpRequest at 'http://localhost:44360/chatHub/negotiate' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

有人能看出我做错了什么吗?

(针对技术问题的询问)

你尝试过代理你的请求吗?https://create-react-app.dev/docs/proxying-api-requests-in-development/ - Shawn Yap
没听说过那个,稍后会看一下,但初步看起来似乎很复杂! - Paul
基本上只需在 package.json 文件中添加 'proxy': 'http://localhost:44360',然后您就可以在不使用 baseUrl(http://localhost:44360) 的情况下调用请求了。 - Shawn Yap
我将其添加到根目录下的package.json中。但是它不起作用,因为它只会转到本地主机:3000 / chathub(显然没有空格)。 - Paul
这个def是与SignalR兼容的吗?看起来它完全被忽略了。我使用了链接中的setupProxy.js方法。 - Paul
4个回答

7

经过几个小时的尝试,我终于让它工作了。

我将在这里保留我的问题和解决方案,以帮助其他人。

首先,在ConfigureServices中:

  public void ConfigureServices(IServiceCollection services)
  {
    services.AddRazorPages();
    services.AddCors();
    services.AddSignalR();
  }

确保 Cors 在 Signal R 之前

然后在 Configure 中进行配置

        // Make sure the CORS middleware is ahead of SignalR.
        app.UseCors(builder =>
        {
            builder.WithOrigins("http://localhost:3000") //Source
                .AllowAnyHeader()
                .WithMethods("GET", "POST")
                .AllowCredentials();
        });

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<MYHubClass>("/myHub");
        });

确保在 UseEndpoints 之前使用 UseCors。

2
问题在于,您在IIS配置和代码中都定义了来源。如果(1)您只信任服务器上所有应用程序的单个来源,或者(2)您信任服务器上所有应用程序的所有来源,则应仅在IIS配置中指定来源。
当在服务器级别的配置中指定时,管道中的每个HTTP上下文都会添加Access-Control-Allow-Origin头。然后,WithOrigins()方法将另一个值附加到其中。

https://www.w3.org/TR/cors/

6.4 实现注意事项 本节非规范性。
希望使自己能够与多个来源共享但不统一响应“*”的资源,在实践中必须动态生成Access-Control-Allow-Origin头以响应他们想要允许的每个请求。因此,这些资源的作者应该发送一个Vary:Origin HTTP头或提供其他适当的控制指令,以防止缓存这样的响应,如果跨源重复使用,可能会出现不准确的情况。

1

尝试像这样设置Cors:

services.AddCors(options =>
{
    options.AddPolicy("CorsPolicy", builder => builder.WithOrigins("http://localhost:3000")
        .SetIsOriginAllowed((host) => true)
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials());
});

1
那不起作用 - 我得到了错误:响应预检请求未通过访问控制检查:“Access-Control-Allow-Origin”标头包含多个值“http://localhost:3000,*”,但只允许一个。 - Paul
1
还有其他的想法吗?我什么都做不了!即使将服务器放入Azure并在本地机器上运行React也无法正常工作。真的非常令人沮丧! - Paul
@Paul 今天我也遇到了同样的问题。经过几个小时的故障排除,我只是从 Azure 中删除了服务并重新创建,CORS 问题就解决了。不知道这是怎么发生的。 - Kiril1512

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接