如何在ASP.NET Core 3.0中使用SignalR

4

我们正在开发一个依赖于SignalR的asp.net core项目。最近我们将项目从ASP.NET Core 2.2更新到了3.0,结果SignalR停止工作了。我们按照文档配置了一切(我认为是正确的),但仍然无法正常工作。我们错过了什么?

ASP.NET Core 3.0 API

启动:

public class Startup
{
     private readonly string corsPolicy = "CorsPolicy";
     private static string[] AllowdOrigins() => return new string[] {"localhost:4200","example.com"};

     public Startup(IConfiguration configuration)
     {
         Configuration = configuration;
     }

     public IConfiguration Configuration { get; }

     public void ConfigureServices(IServiceCollection services)
     {
            ConfigureAuthentication(services);

            ///MICROSOFT SQL DATABASE
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")
            ));

            services.Configure<ApiBehaviorOptions>(options =>
            {
                options.SuppressModelStateInvalidFilter = true;
            });

            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.KnownProxies.Add(IPAddress.Parse("XX.XX.XX.XX"));
            });

            services.AddSignalR();
            services.AddControllers();

            //services dependencies
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseCors(corsPolicy);

            //app.UseForwardedHeaders(new ForwardedHeadersOptions
            //{
            //    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            //});

            app.UseAuthentication();
            app.UseAuthorization();

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

            DummyData.Initialize(app);
        }

        private void ConfigureAuthentication(IServiceCollection services)
        {
            services.AddCors(options =>
            {
                options.AddPolicy(corsPolicy,
                builder =>
                {
                    builder
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowAnyOrigin()
                    .AllowCredentials()
                    .WithOrigins(AllowdOrigins());
                });
            });

            services.AddHttpContextAccessor();

            // configure strongly typed settings objects
            var appSettingsSection = Configuration.GetSection("AppSettings");
            services.Configure<AppSettings>(appSettingsSection);

            // configure jwt authentication
            var appSettings = appSettingsSection.Get<AppSettings>();
            var key = Encoding.ASCII.GetBytes(appSettings.Secret);

            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });
        }
}

聊天中心:

[EnableCors("CorsPolicy")]
public class ChatHub : Hub
{
    private static Dictionary<string, int> onlineClientCounts = new Dictionary<string, int>();
    private static readonly string FrontPrefix = "_FRONT";

    public ChatHub()
    {
    }

    [HubMethodName("ConnectFrontend")]
    public async Task ConnectFrontend(int sessionId)
    {
        //////
    }

    [HubMethodName("ConnectDevice")]
    public async Task ConnectDevice(int sessionId)
    {
        //// This method should be called but it isn't.
    }

    public void DisConnect(int sessionId, int userId)
    {
       //////////
    }

    [HubMethodName("SendJsonToFrontends")]
    public async Task SendJsonToFrontends(int sessionId, string jsonString)
    {
        ///
    }

    [HubMethodName("SendJsonToAll")]
    public async Task SendJsonToAll(int sessionId, string jsonString)
    {
         ////
    }
}

Angular 项目

SignalRService

export class SignalRService {

  private connection: signalR.HubConnection;

  public newMessage = new Subject<SocketMessage>();

  constructor() {
    
  }

  public connectFront() {

    this.connection = new signalR.HubConnectionBuilder()
        .withUrl("http://localhost:2525/chat")//(environment.baseSignalR)
        .configureLogging(signalR.LogLevel.Trace)
        .build();


    this.connection.on("ReceiveJson", data => { this.handleJsonMessage(data) });

    // handles the first connection message
    this.connection.start().then(() => this.sendConnectionMessage());

    // handles the incoming messages
    this.connection.on("ReceiveJson", data => this.handleJsonMessage(data));
    this.connection.on("ReceiveJsonFrontend", data => this.handleJsonMessage(data));
  }

  private sendConnectionMessage() {
    var sessionId = sessionStorage.getItem("SessionId");
    if (sessionId != null) {
        this.connection.invoke("ConnectFrontend", sessionId).catch((error) => { debugger; console.log(error); });
    }
  }

  public sendWebsocketMessageToAll(msg: SocketMessage) {
    var sessionId = sessionStorage.getItem("SessionId");
    if (sessionId != null) {
      this.connection.invoke("SendJsonToAll", sessionId, JSON.stringify(msg)).catch((error) => console.log(error));
    }
  }

  public sendWebsocketMessageToFrontend(msg: SocketMessage) {
    var sessionId = sessionStorage.getItem("SessionId");
    if (sessionId != null) {
      this.connection.invoke("SendJsonToFrontends", sessionId, JSON.stringify(msg)).catch((error) => console.log(error));
    }
  }

  private handleJsonMessage(data: string) {
    this.newMessage.next(this.getSocketMessage(data));
  }

  public getSocketMessage(data: string): SocketMessage {
    var msg: SocketMessage = JSON.parse(data);
    return msg;
  }

  public disconnect() {
    this.connection.stop();
  }
}

Angular输出: 输入图像说明

API输出: 输入图像说明


1
你的控制台有没有报错?如果你还没有升级到3.0版本,你也需要将Angular SignalR包升级。 - User3250
我已经更新了我的问题,并附上了控制台日志。 - Ties Theunissen
这个有更新了吗? - Tony Ngo
抱歉,我只在周二和周四处理这个问题。明天我会尝试一切。 - Ties Theunissen
显示剩余3条评论
3个回答

2

我最终在Tony的帮助下找到了解决方法,

显然,在SignalR聊天中心的方法中出现了问题。这些方法只允许整数作为参数,但实际上需要字符串。我不知道是否与其他设置有关,但我的猜测是signalr无法将请求参数转换为除字符串以外的其他类型。

当我把参数改成字符串时,它就可以工作了。


2

只需使用Angular模板创建一个Web应用程序API,您可以查看我的代码以供参考。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp/dist";
        });
        services.AddSignalR().AddJsonProtocol(options =>
        {
            options.PayloadSerializerOptions.WriteIndented = false;
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
        }

        app.UseStaticFiles();
        if (!env.IsDevelopment())
        {
            app.UseSpaStaticFiles();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}");
            endpoints.MapHub<ChatHub>("/chatHub");
        });

        app.UseSpa(spa =>
        {
            spa.Options.SourcePath = "ClientApp";

            if (env.IsDevelopment())
            {
                spa.UseAngularCliServer(npmScript: "start");
            }
        });
    }

在前端部分。注意:使用新的包 @microsoft/signalr

import * as signalR from "@microsoft/signalr";
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
    title = 'app';

    ngOnInit() {
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("/chathub")
            .build();

        connection.on("receiveMessage", (username: string, message: string) => {
            console.log(username);
            console.log(message);
        });

        connection.start().then(() => {
            connection.send("sendMessage", "aaa", "aaaa")
                .then(() => console.log("done"));

        }).catch(err => document.write(err));
    }
}

1
似乎您已经在服务器端配置了基于JWT的身份验证,但没有为SignalR连接提供令牌。尝试使用accessTokenFactory提供令牌:
this.hubConnection = new signalR.HubConnectionBuilder()
            .withUrl(`${environment.urlAddress}/chathub`, {
                accessTokenFactory: () => this.token
            })
            .build()

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