Я пытаюсь подключиться к SignalR на своем бэкэнде, но это не работает

В своем проекте я использую Vue + ts + @microsoft/signalr. Я хочу разработать чат с SignalR. Я также использую C# + ASP.NET Core в серверной части + докер.

Итак, я написал следующий код на своем бэкэнде:

using DomovoiBackend.API.Auth.ServiceDecorators;
using DomovoiBackend.API.Controllers;
using DomovoiBackend.API.JsonInheritance;
using DomovoiBackend.Application;
using DomovoiBackend.Application.Services.CounterAgentServices.Interfaces;
using DomovoiBackend.Persistence;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSignalR();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1",
        new OpenApiInfo()
        {
            Title = "DomovoiBackend.API",
            Version = "v1"
        });
    options.UseAllOfToExtendReferenceSchemas();
    options.UseAllOfForInheritance();
    options.UseOneOfForPolymorphism();
});

var inheritanceConfigurations =
    new AssemblyInheritanceConfiguration()
        .CreateAllConfigurations();

builder.Services.AddControllers().AddNewtonsoftJson(
    options =>
    {
        foreach(var inheritanceConfiguration in inheritanceConfigurations)
            options.SerializerSettings.Converters.Add(inheritanceConfiguration);
    });

builder.Services.AddApplicationLayer()
    .AddMappers()
    .AddPersistence(builder.Configuration)
    .CreateDatabase()
    .FillDatabase();

builder.Services.Decorate<ICounterAgentService, AuthCounterAgentService>();

builder.Services.AddHttpContextAccessor();

builder.Services.AddDistributedMemoryCache();

builder.Services.AddAuthorization();
builder.Services.AddSession();

builder.Services.AddCors();

builder.Services
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
        options.SlidingExpiration = true;
        options.LoginPath = "/CounterAgent/Login";
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.Strict;
    });

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("AuthenticatedCounterAgent", policy => policy.RequireClaim("CounterAgentId"));

var app = builder.Build();

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

app.UseCors(policyBuilder =>
{
    policyBuilder.WithOrigins("http://localhost:5173")
        .AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials()
        .SetIsOriginAllowedToAllowWildcardSubdomains()
        .SetIsOriginAllowed((host) => true);
});

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

app.MapControllers();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapHub<ChatHub>("/chat");

app.Run();

public partial class Program;

Класс ЧатХаба:

 [Authorize]
 public class ChatHub : Hub
 {
     [Authorize]
     public async Task Send(string message, string idReceiver)
     {
         var nameSender = Context.User?.Identity?.Name;
         if (Clients.User(idReceiver) == null)
             throw new UnknownUser($"User with id \"{idReceiver}\" does not exist in the system");
         await Clients.User(idReceiver).SendAsync("Receive", message, nameSender);
         await Clients.Caller.SendAsync("NotifySendMethod", "Ok");
     }
 }

и интерфейс:

import  { HttpTransportType, HubConnection, HubConnectionBuilder } from "@microsoft/signalr";
import { inject, singleton } from "tsyringe";
import { Chat } from "../../application/useCases/chat";
import { Message } from "../../domain/chat/message";
import { MessageStatus } from "../../domain/enums/chatEnum";
import { CounterAgent } from "../../domain/counteragents/counteragent";
import { CounteragentViewModel } from "../../viewModel/CounteragentViewModel";
import { ICouterAgentMapper } from "../../mappers/interfaces/couteragentMapperInterface";

@singleton()
export class ChatService {

    private readonly _connection: HubConnection;
    private _listener!: Chat;
    
    public constructor(@inject("chatURL") private readonly _baseURL: string,
        @inject("ICouterAgentMapper") private readonly _userMapper: ICouterAgentMapper) {
        this._connection = new HubConnectionBuilder()
            .withUrl(this._baseURL, {
                headers: { 
                    'Access-Control-Allow-Origin': '*',
                 },
                withCredentials: true,
                transport: HttpTransportType.WebSockets,
            })
            .build();
        this._connection.on("Receive", (text: string, idSender: string) => this.receiveMessage(text, idSender));
    }

    public start(idCompanion :string, user: CounteragentViewModel): void {
        let counteragent = this._userMapper.mapViewModelToCouterAget(user);
        this._listener = new Chat(this, true, idCompanion, counteragent);
        this._connection.start();
    }

    public close(): void {
        this._connection.stop()
    }

    public get state(): string {
        return this._connection.state.toString();
    }

    public get messages(): Message[] {
        return this._listener.messages;
    }

    public async sendMessage(text: string): Promise<void> {
        let message = new Message("", text, this._listener.idCompanion, this._listener.user.id!);
        try {
            let response = await this._connection.invoke("Send", message.text, message.recieverId);
            message.status = MessageStatus.Send;            
        }
        catch(e){
            message.status = MessageStatus.NotSend;
            throw(e);
        }
        finally {
            this._listener.addMessage(message);
        }
    }

    public receiveMessage(text: string, idSender: string): void {
        let message = new Message("", text, idSender, this._listener.user.id!);
        message.status = MessageStatus.Recieve;
        this._listener.addMessage(message);
    } 
}

Я использую ChatService в ChatView.vue:

//...
 export default defineComponent({
        components: { Header},
        data() {
            return {
                store: store,
                chatService: {} as ChatService, 
                //...
            };
        },
        computed: {
        },
        mounted() {
        //...
            this.load_data();
        },
        methods: {
            load_data(){
                let chatService = container.resolve(ChatService);
                this.chatService = chatService;
                chatService.start(this.user.id, this.store.state.user!);
                this.messages = this.chatService.messages;
            },
        //...
        },
    })
    </script>

После метода load_data() и Connection.start() я получаю это сообщение:

Доступ для получения на 'http://localhost:8181/chat/negotiate?negotiateVersion=1' из источника «http://localhost:5173» заблокирован политикой CORS: ответ на предполетный запрос не проходит проверку контроля доступа: перенаправление не разрешен предполетный запрос.

@microsoft_signalr.js?v=12bba02c:2110 Неперехвачено (обещано) Ошибка: Не удалось завершить согласование с сервером: Ошибка типа: Не удалось выполнить принести в HttpConnection._getNegotiationResponse (@microsoft_signalr.js?v=12bba02c:2110:29)

ChatService.ts:35 [2024-06-03T18:17:15.726Z] Ошибка: не удалось запустить соединение: Ошибка: не удалось завершить согласование с сервером: Ошибка типа: не удалось получить

ChatService.ts:35 [2024-06-03T18:17:15.725Z] Ошибка: не удалось

для завершения согласования с сервером: TypeError: Не удалось получить

Я использовал разные настройки политики cors на бэкэнде и разные заголовки в ConnectionBuilder.WithUrl(...), но мне это не помогает.

Итак, проблема CORS. Пожалуйста, посмотрите это в Интернете. Поскольку вы все еще можете пропустить эту часть, вот один из моих ответов, суммирующий все это: stackoverflow.com/a/72211930/8816585

kissu 03.06.2024 22:25
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
1
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы используете SetIsOriginAllowed и WithOrigins вместе. Возникнет проблема с корсом, более подробную информацию вы можете проверить в этой теме.

Измените настройку CORS, как показано ниже.

app.UseCors(policyBuilder =>
{
    policyBuilder.WithOrigins("http://localhost:5173")
        .AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials();
        //.SetIsOriginAllowedToAllowWildcardSubdomains()
        //.SetIsOriginAllowed((host) => true);
});

И я также обнаружил в вашем коде некоторую проблему с порядком промежуточного программного обеспечения.

var app = builder.Build();
    
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//app.UseAuthentication();
//app.UseAuthorization();
//app.UseSession();

app.UseCors(policyBuilder =>
{
    policyBuilder.WithOrigins("http://localhost:5173")
        .AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials();
        //.SetIsOriginAllowedToAllowWildcardSubdomains()
        //.SetIsOriginAllowed((host) => true);
});

app.UseAuthentication();
app.UseAuthorization();
// move it to here
app.UseSession();

app.MapControllers();


app.MapHub<ChatHub>("/chat");

app.Run();

Другие вопросы по теме