* Move startup action config into AddServerSideBlazor, so that UseServerSideBlazor is reduced to trivial shorthand that can become optional * Make BlazorHub public so developers can use it with UseAzureSignalR * Move BlazorHub to Microsoft.AspNetCore.Blazor.Server namespace for easier consumption * Add notes * Have E2E tests validate that devs don't have to call UseServerSideBlazor * Add forgotten tweak * CR: Document that BlazorHub methods are not intended for application use. * CR: Rename extension method to UseSignalRWithBlazorHub * CR: TryAdd
This commit is contained in:
parent
d4cbb86f46
commit
6ff3674b16
|
|
@ -3,40 +3,59 @@
|
|||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Blazor.Server.Circuits;
|
||||
using Microsoft.AspNetCore.Blazor.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.Circuits
|
||||
namespace Microsoft.AspNetCore.Blazor.Server
|
||||
{
|
||||
internal class BlazorHub : Hub
|
||||
/// <summary>
|
||||
/// A SignalR hub that accepts connections to a Server-Side Blazor app.
|
||||
/// </summary>
|
||||
public sealed class BlazorHub : Hub
|
||||
{
|
||||
private static readonly object CircuitKey = new object();
|
||||
|
||||
private readonly CircuitFactory _circuitFactory;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Intended for framework use only. Applications should not instantiate
|
||||
/// this class directly.
|
||||
/// </summary>
|
||||
public BlazorHub(
|
||||
ILogger<BlazorHub> logger,
|
||||
CircuitFactory circuitFactory)
|
||||
IServiceProvider services)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_circuitFactory = circuitFactory ?? throw new ArgumentNullException(nameof(circuitFactory));
|
||||
_circuitFactory = services.GetRequiredService<CircuitFactory>();
|
||||
}
|
||||
|
||||
public CircuitHost CircuitHost
|
||||
/// <summary>
|
||||
/// Gets the default endpoint path for incoming connections.
|
||||
/// </summary>
|
||||
public static PathString DefaultPath => "/_blazor";
|
||||
|
||||
private CircuitHost CircuitHost
|
||||
{
|
||||
get => (CircuitHost)Context.Items[CircuitKey];
|
||||
set => Context.Items[CircuitKey] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intended for framework use only. Applications should not call this method directly.
|
||||
/// </summary>
|
||||
public override Task OnDisconnectedAsync(Exception exception)
|
||||
{
|
||||
CircuitHost.Dispose();
|
||||
return base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intended for framework use only. Applications should not call this method directly.
|
||||
/// </summary>
|
||||
public async Task StartCircuit(string uriAbsolute, string baseUriAbsolute)
|
||||
{
|
||||
var circuitHost = _circuitFactory.CreateCircuitHost(Context.GetHttpContext(), Clients.Caller);
|
||||
|
|
@ -50,7 +69,10 @@ namespace Microsoft.AspNetCore.Blazor.Server.Circuits
|
|||
await circuitHost.InitializeAsync();
|
||||
CircuitHost = circuitHost;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Intended for framework use only. Applications should not call this method directly.
|
||||
/// </summary>
|
||||
public void BeginInvokeDotNetFromJS(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson)
|
||||
{
|
||||
EnsureCircuitHost().BeginInvokeDotNetFromJS(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson);
|
||||
|
|
@ -24,12 +24,13 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// Configures the middleware pipeline to work with Blazor.
|
||||
/// </summary>
|
||||
/// <typeparam name="TProgram">Any type from the client app project. This is used to identify the client app assembly.</typeparam>
|
||||
/// <param name="app"></param>
|
||||
public static void UseBlazor<TProgram>(
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
|
||||
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
|
||||
public static IApplicationBuilder UseBlazor<TProgram>(
|
||||
this IApplicationBuilder app)
|
||||
{
|
||||
var clientAssemblyInServerBinDir = typeof(TProgram).Assembly;
|
||||
app.UseBlazor(new BlazorOptions
|
||||
return app.UseBlazor(new BlazorOptions
|
||||
{
|
||||
ClientAssemblyPath = clientAssemblyInServerBinDir.Location,
|
||||
});
|
||||
|
|
@ -38,9 +39,10 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// <summary>
|
||||
/// Configures the middleware pipeline to work with Blazor.
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="options"></param>
|
||||
public static void UseBlazor(
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
|
||||
/// <param name="options">Options to configure the middleware.</param>
|
||||
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
|
||||
public static IApplicationBuilder UseBlazor(
|
||||
this IApplicationBuilder app,
|
||||
BlazorOptions options)
|
||||
{
|
||||
|
|
@ -108,6 +110,8 @@ namespace Microsoft.AspNetCore.Builder
|
|||
spa.Options.DefaultPageStaticFileOptions = indexHtmlStaticFileOptions;
|
||||
});
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static string FindIndexHtmlFile(BlazorConfig config)
|
||||
|
|
|
|||
|
|
@ -2,15 +2,14 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Blazor.Builder;
|
||||
using Microsoft.AspNetCore.Blazor.Hosting;
|
||||
using Microsoft.AspNetCore.Blazor.Server.Circuits;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.AspNetCore.Blazor.Server;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods to configure an <see cref="IApplicationBuilder"/> for Server-Side Blazor.
|
||||
/// These are just shorthand for combining UseSignalR with UseBlazor.
|
||||
/// </summary>
|
||||
public static class ServerSideBlazorApplicationBuilderExtensions
|
||||
{
|
||||
|
|
@ -20,94 +19,58 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// <param name="builder">The <see cref="IApplicationBuilder"/>.</param>
|
||||
/// <typeparam name="TStartup">A Blazor startup type.</typeparam>
|
||||
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
|
||||
public static IApplicationBuilder UseServerSideBlazor<TStartup>(this IApplicationBuilder builder)
|
||||
public static IApplicationBuilder UseServerSideBlazor<TStartup>(
|
||||
this IApplicationBuilder builder)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
return UseServerSideBlazor(builder, typeof(TStartup));
|
||||
// WARNING: Don't add extra setup logic here. It's important for
|
||||
// UseServerSideBlazor just to be shorthand for UseSignalR+UseBlazor,
|
||||
// so that people who want to call those two manually instead can
|
||||
// also do so. That's needed for people using Azure SignalR.
|
||||
|
||||
// TODO: Also allow configuring the endpoint path.
|
||||
return UseSignalRWithBlazorHub(builder, BlazorHub.DefaultPath)
|
||||
.UseBlazor<TStartup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers Server-Side Blazor in the pipeline.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IApplicationBuilder"/>.</param>
|
||||
/// <param name="startupType">A Blazor startup type.</param>
|
||||
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
|
||||
public static IApplicationBuilder UseServerSideBlazor(
|
||||
this IApplicationBuilder builder,
|
||||
Type startupType)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (startupType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(startupType));
|
||||
}
|
||||
|
||||
var startup = builder.ApplicationServices.GetRequiredService(startupType);
|
||||
var wrapper = new ConventionBasedStartup(startup);
|
||||
Action<IBlazorApplicationBuilder> configure = (b) =>
|
||||
{
|
||||
wrapper.Configure(b, b.Services);
|
||||
};
|
||||
|
||||
UseServerSideBlazorCore(builder, configure);
|
||||
|
||||
builder.UseBlazor(new BlazorOptions()
|
||||
{
|
||||
ClientAssemblyPath = startupType.Assembly.Location,
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers middleware for Server-Side Blazor.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IApplicationBuilder"/>.</param>
|
||||
/// <param name="options">A <see cref="BlazorOptions"/> instance used to configure the Blazor file provider.</param>
|
||||
/// <param name="startupAction">A delegate used to configure the renderer.</param>
|
||||
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
|
||||
public static IApplicationBuilder UseServerSideBlazor(
|
||||
this IApplicationBuilder builder,
|
||||
BlazorOptions options,
|
||||
Action<IBlazorApplicationBuilder> startupAction)
|
||||
BlazorOptions options)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (startupAction == null)
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(startupAction));
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
UseServerSideBlazorCore(builder, startupAction);
|
||||
// WARNING: Don't add extra setup logic here. It's important for
|
||||
// UseServerSideBlazor just to be shorthand for UseSignalR+UseBlazor,
|
||||
// so that people who want to call those two manually instead can
|
||||
// also do so. That's needed for people using Azure SignalR.
|
||||
|
||||
builder.UseBlazor(options);
|
||||
|
||||
return builder;
|
||||
// TODO: Also allow configuring the endpoint path.
|
||||
return UseSignalRWithBlazorHub(builder, BlazorHub.DefaultPath)
|
||||
.UseBlazor(options);
|
||||
}
|
||||
|
||||
private static IApplicationBuilder UseServerSideBlazorCore(
|
||||
IApplicationBuilder builder,
|
||||
Action<IBlazorApplicationBuilder> configure)
|
||||
private static IApplicationBuilder UseSignalRWithBlazorHub(
|
||||
IApplicationBuilder builder, PathString path)
|
||||
{
|
||||
var endpoint = "/_blazor";
|
||||
|
||||
var factory = (DefaultCircuitFactory)builder.ApplicationServices.GetRequiredService<CircuitFactory>();
|
||||
factory.StartupActions.Add(endpoint, configure);
|
||||
|
||||
builder.UseSignalR(route => route.MapHub<BlazorHub>(endpoint));
|
||||
|
||||
return builder;
|
||||
return builder.UseSignalR(route => route.MapHub<BlazorHub>(BlazorHub.DefaultPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,32 +2,36 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Blazor.Browser.Rendering;
|
||||
using Microsoft.AspNetCore.Blazor.Builder;
|
||||
using Microsoft.AspNetCore.Blazor.Rendering;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.Circuits
|
||||
{
|
||||
internal class DefaultCircuitFactory : CircuitFactory
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly DefaultCircuitFactoryOptions _options;
|
||||
|
||||
public DefaultCircuitFactory(IServiceScopeFactory scopeFactory)
|
||||
public DefaultCircuitFactory(
|
||||
IServiceScopeFactory scopeFactory,
|
||||
IOptions<DefaultCircuitFactoryOptions> options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
|
||||
|
||||
StartupActions = new Dictionary<PathString, Action<IBlazorApplicationBuilder>>();
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
public Dictionary<PathString, Action<IBlazorApplicationBuilder>> StartupActions { get; }
|
||||
|
||||
public override CircuitHost CreateCircuitHost(HttpContext httpContext, IClientProxy client)
|
||||
{
|
||||
if (!StartupActions.TryGetValue(httpContext.Request.Path, out var config))
|
||||
if (!_options.StartupActions.TryGetValue(httpContext.Request.Path, out var config))
|
||||
{
|
||||
var message = $"Could not find a Blazor startup action for request path {httpContext.Request.Path}";
|
||||
throw new InvalidOperationException(message);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Blazor.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.Circuits
|
||||
{
|
||||
internal class DefaultCircuitFactoryOptions
|
||||
{
|
||||
// During the DI configuration phase, we use Configure<DefaultCircuitFactoryOptions>(...)
|
||||
// callbacks to build up this dictionary mapping paths to startup actions
|
||||
public Dictionary<PathString, Action<IBlazorApplicationBuilder>> StartupActions { get; }
|
||||
|
||||
public DefaultCircuitFactoryOptions()
|
||||
{
|
||||
StartupActions = new Dictionary<PathString, Action<IBlazorApplicationBuilder>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Blazor;
|
||||
using Microsoft.AspNetCore.Blazor.Hosting;
|
||||
using Microsoft.AspNetCore.Blazor.Server;
|
||||
using Microsoft.AspNetCore.Blazor.Server.Circuits;
|
||||
using Microsoft.AspNetCore.Blazor.Services;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
|
|
@ -20,32 +21,15 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||
public static IServiceCollection AddServerSideBlazor(this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
return AddServerSideBlazor(services, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Server-Side Blazor services to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <param name="configure">A delegate to configure the <see cref="ServerSideBlazorOptions"/>.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||
public static IServiceCollection AddServerSideBlazor(
|
||||
this IServiceCollection services,
|
||||
Action<ServerSideBlazorOptions> configure)
|
||||
this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
return AddServerSideBlazorCore(services, null, configure);
|
||||
return AddServerSideBlazor(services, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -54,7 +38,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <param name="startupType">A Blazor startup type.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||
public static IServiceCollection AddServerSideBlazor(this IServiceCollection services, Type startupType)
|
||||
public static IServiceCollection AddServerSideBlazor(
|
||||
this IServiceCollection services,
|
||||
Type startupType)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
|
|
@ -66,7 +52,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
throw new ArgumentNullException(nameof(startupType));
|
||||
}
|
||||
|
||||
return AddServerSideBlazorCore(services, startupType, null);
|
||||
return AddServerSideBlazorCore(services, startupType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -75,101 +61,75 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <typeparam name="TStartup">A Blazor startup type.</typeparam>
|
||||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||
public static IServiceCollection AddServerSideBlazor<TStartup>(this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
return AddServerSideBlazorCore(services, typeof(TStartup), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Server-Side Blazor services to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <param name="startupType">A Blazor startup type.</param>
|
||||
/// <param name="configure">A delegate to configure the <see cref="ServerSideBlazorOptions"/>.</param>
|
||||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||
public static IServiceCollection AddServerSideBlazor(
|
||||
this IServiceCollection services,
|
||||
Type startupType,
|
||||
Action<ServerSideBlazorOptions> configure)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (startupType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(startupType));
|
||||
}
|
||||
|
||||
return AddServerSideBlazorCore(services, startupType, configure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Server-Side Blazor services to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
|
||||
/// <param name="configure">A delegate to configure the <see cref="ServerSideBlazorOptions"/>.</param>
|
||||
/// <typeparam name="TStartup">A Blazor startup type.</typeparam>
|
||||
/// <returns>The <see cref="IServiceCollection"/>.</returns>
|
||||
public static IServiceCollection AddServerSideBlazor<TStartup>(
|
||||
this IServiceCollection services,
|
||||
Action<ServerSideBlazorOptions> configure)
|
||||
this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
return AddServerSideBlazorCore(services, typeof(TStartup), configure);
|
||||
return AddServerSideBlazorCore(services, typeof(TStartup));
|
||||
}
|
||||
|
||||
private static IServiceCollection AddServerSideBlazorCore(
|
||||
IServiceCollection services,
|
||||
Type startupType,
|
||||
Action<ServerSideBlazorOptions> configure)
|
||||
Type startupType)
|
||||
{
|
||||
services.AddSingleton<CircuitFactory, DefaultCircuitFactory>();
|
||||
services.AddScoped<ICircuitAccessor, DefaultCircuitAccessor>();
|
||||
services.AddScoped<Circuit>(s => s.GetRequiredService<ICircuitAccessor>().Circuit);
|
||||
|
||||
services.AddScoped<IJSRuntimeAccessor, DefaultJSRuntimeAccessor>();
|
||||
services.AddScoped<IJSRuntime>(s => s.GetRequiredService<IJSRuntimeAccessor>().JSRuntime);
|
||||
|
||||
services.AddScoped<IUriHelper, RemoteUriHelper>();
|
||||
|
||||
services.AddSignalR().AddMessagePackProtocol(options =>
|
||||
{
|
||||
// TODO: Enable compression, either by having SignalR use
|
||||
// LZ4MessagePackSerializer instead of MessagePackSerializer,
|
||||
// or perhaps by compressing the RenderBatch bytes ourselves
|
||||
// and then using https://github.com/nodeca/pako in JS to decompress.
|
||||
options.FormatterResolvers.Insert(0, new RenderBatchFormatterResolver());
|
||||
});
|
||||
AddStandardServerSideBlazorServices(services);
|
||||
|
||||
if (startupType != null)
|
||||
{
|
||||
// Make sure we only create a single instance of the startup type. We can register
|
||||
// it in the services so we can retrieve it later when creating the middlware.
|
||||
// Call TStartup's ConfigureServices method immediately
|
||||
var startup = Activator.CreateInstance(startupType);
|
||||
services.AddSingleton(startupType, startup);
|
||||
|
||||
// We don't need to reuse the wrapper, it's not stateful.
|
||||
var wrapper = new ConventionBasedStartup(startup);
|
||||
wrapper.ConfigureServices(services);
|
||||
}
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
services.Configure(configure);
|
||||
// Configure the circuit factory to call a startup action when each
|
||||
// incoming connection is established. The startup action is "call
|
||||
// TStartup's Configure method".
|
||||
services.Configure<DefaultCircuitFactoryOptions>(circuitFactoryOptions =>
|
||||
{
|
||||
var endpoint = BlazorHub.DefaultPath; // TODO: allow configuring this
|
||||
if (circuitFactoryOptions.StartupActions.ContainsKey(endpoint))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Multiple Server Side Blazor entries are configured to use " +
|
||||
$"the same endpoint '{endpoint}'.");
|
||||
}
|
||||
|
||||
circuitFactoryOptions.StartupActions.Add(endpoint, builder =>
|
||||
{
|
||||
wrapper.Configure(builder, builder.Services);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void AddStandardServerSideBlazorServices(IServiceCollection services)
|
||||
{
|
||||
// Here we add a bunch of services that don't vary in any way based on the
|
||||
// user's configuration. So even if the user has multiple independent server-side
|
||||
// Blazor entrypoints, this lot is the same and repeated registrations are a no-op.
|
||||
services.TryAddSingleton<CircuitFactory, DefaultCircuitFactory>();
|
||||
services.TryAddScoped<ICircuitAccessor, DefaultCircuitAccessor>();
|
||||
services.TryAddScoped<Circuit>(s => s.GetRequiredService<ICircuitAccessor>().Circuit);
|
||||
services.TryAddScoped<IJSRuntimeAccessor, DefaultJSRuntimeAccessor>();
|
||||
services.TryAddScoped<IJSRuntime>(s => s.GetRequiredService<IJSRuntimeAccessor>().JSRuntime);
|
||||
services.TryAddScoped<IUriHelper, RemoteUriHelper>();
|
||||
|
||||
// We've discussed with the SignalR team and believe it's OK to have repeated
|
||||
// calls to AddSignalR (making the nonfirst ones no-ops). If we want to change
|
||||
// this in the future, we could change AddServerSideBlazor to be an extension
|
||||
// method on ISignalRServerBuilder so the developer always has to chain it onto
|
||||
// their own AddSignalR call. For now we're keeping it like this because it's
|
||||
// simpler for developers in common cases.
|
||||
services.AddSignalR().AddMessagePackProtocol(options =>
|
||||
{
|
||||
options.FormatterResolvers.Insert(0, new RenderBatchFormatterResolver());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for Server-Side Blazor.
|
||||
/// </summary>
|
||||
public class ServerSideBlazorOptions
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using Microsoft.AspNetCore.Blazor.Server;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
|
@ -39,7 +40,14 @@ namespace TestServer
|
|||
// Mount the server-side Blazor app on /subdir
|
||||
app.Map("/subdir", subdirApp =>
|
||||
{
|
||||
subdirApp.UseServerSideBlazor<BasicTestApp.Startup>();
|
||||
// The following two lines are equivalent to:
|
||||
// subdirApp.UseServerSideBlazor<BasicTestApp.Startup>();
|
||||
// However it's expressed using UseSignalR+UseBlazor as a way of checking that
|
||||
// we're not relying on any extra magic inside UseServerSideBlazor, since it's
|
||||
// important that people can set up these bits of middleware manually (e.g., to
|
||||
// swap in UseAzureSignalR instead of UseSignalR).
|
||||
subdirApp.UseSignalR(route => route.MapHub<BlazorHub>(BlazorHub.DefaultPath));
|
||||
subdirApp.UseBlazor<BasicTestApp.Startup>();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue