File: HubEndpointRouteBuilderExtensions.cs
Web Access
Project: src\src\SignalR\server\SignalR\src\Microsoft.AspNetCore.SignalR.csproj (Microsoft.AspNetCore.SignalR)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
 
namespace Microsoft.AspNetCore.Builder;
 
/// <summary>
/// Extension methods on <see cref="IEndpointRouteBuilder"/> to add routes to <see cref="Hub"/>s.
/// </summary>
public static class HubEndpointRouteBuilderExtensions
{
    private const DynamicallyAccessedMemberTypes HubAccessibility = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods;
 
    /// <summary>
    /// Maps incoming requests with the specified path to the specified <see cref="Hub"/> type.
    /// </summary>
    /// <typeparam name="THub">The <see cref="Hub"/> type to map requests to.</typeparam>
    /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
    /// <param name="pattern">The route pattern.</param>
    /// <returns>An <see cref="HubEndpointConventionBuilder"/> for endpoints associated with the connections.</returns>
    public static HubEndpointConventionBuilder MapHub<[DynamicallyAccessedMembers(HubAccessibility)] THub>(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern) where THub : Hub
    {
        return endpoints.MapHub<THub>(pattern, configureOptions: null);
    }
 
    /// <summary>
    /// Maps incoming requests with the specified path to the specified <see cref="Hub"/> type.
    /// </summary>
    /// <typeparam name="THub">The <see cref="Hub"/> type to map requests to.</typeparam>
    /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
    /// <param name="pattern">The route pattern.</param>
    /// <param name="configureOptions">A callback to configure dispatcher options.</param>
    /// <returns>An <see cref="HubEndpointConventionBuilder"/> for endpoints associated with the connections.</returns>
    public static HubEndpointConventionBuilder MapHub<[DynamicallyAccessedMembers(HubAccessibility)] THub>(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, Action<HttpConnectionDispatcherOptions>? configureOptions) where THub : Hub
    {
        var marker = endpoints.ServiceProvider.GetService<SignalRMarkerService>();
 
        if (marker == null)
        {
            throw new InvalidOperationException("Unable to find the required services. Please add all the required services by calling " +
                                                "'IServiceCollection.AddSignalR' inside the call to 'ConfigureServices(...)' in the application startup code.");
        }
 
        var options = new HttpConnectionDispatcherOptions();
        configureOptions?.Invoke(options);
 
        var conventionBuilder = endpoints.MapConnections(pattern, options, b =>
        {
            b.UseHub<THub>();
        });
 
        var attributes = typeof(THub).GetCustomAttributes(inherit: true);
        conventionBuilder.Add(e =>
        {
            // Add all attributes on the Hub as metadata (this will allow for things like)
            // auth attributes and cors attributes to work seamlessly
            foreach (var item in attributes)
            {
                e.Metadata.Add(item);
            }
 
            // Add metadata that captures the hub type this endpoint is associated with
            e.Metadata.Add(new HubMetadata(typeof(THub)));
        });
 
        return new HubEndpointConventionBuilder(conventionBuilder);
    }
}