Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Commit 87eb919

Browse files
Following CR feedback, reintroduce ISpaBuilder concept
1 parent 29e97bf commit 87eb919

12 files changed

+111
-110
lines changed

src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliBuilder.cs

+6-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using Microsoft.AspNetCore.NodeServices.Npm;
66
using Microsoft.AspNetCore.NodeServices.Util;
77
using Microsoft.AspNetCore.SpaServices.Prerendering;
8-
using Microsoft.Extensions.Logging;
98
using System;
109
using System.IO;
1110
using System.Text.RegularExpressions;
@@ -37,22 +36,17 @@ public AngularCliBuilder(string npmScript)
3736
}
3837

3938
/// <inheritdoc />
40-
public Task Build(IApplicationBuilder app)
39+
public Task Build(ISpaBuilder spaBuilder)
4140
{
42-
var spaOptions = DefaultSpaOptions.FindInPipeline(app);
43-
if (spaOptions == null)
41+
var sourcePath = spaBuilder.Options.SourcePath;
42+
if (string.IsNullOrEmpty(sourcePath))
4443
{
45-
throw new InvalidOperationException($"{nameof(AngularCliBuilder)} can only be used in an application configured with {nameof(SpaApplicationBuilderExtensions.UseSpa)}().");
44+
throw new InvalidOperationException($"To use {nameof(AngularCliBuilder)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
4645
}
4746

48-
if (string.IsNullOrEmpty(spaOptions.SourcePath))
49-
{
50-
throw new InvalidOperationException($"To use {nameof(AngularCliBuilder)}, you must supply a non-empty value for the {nameof(ISpaOptions.SourcePath)} property of {nameof(ISpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
51-
}
52-
53-
var logger = AngularCliMiddleware.GetOrCreateLogger(app);
47+
var logger = AngularCliMiddleware.GetOrCreateLogger(spaBuilder.ApplicationBuilder);
5448
var npmScriptRunner = new NpmScriptRunner(
55-
spaOptions.SourcePath,
49+
sourcePath,
5650
_npmScriptName,
5751
"--watch");
5852
npmScriptRunner.AttachToLogger(logger);

src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliMiddleware.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ internal static class AngularCliMiddleware
2222
private const int TimeoutMilliseconds = 50 * 1000;
2323

2424
public static void Attach(
25-
IApplicationBuilder appBuilder,
26-
string sourcePath,
25+
ISpaBuilder spaBuilder,
2726
string npmScriptName)
2827
{
28+
var sourcePath = spaBuilder.Options.SourcePath;
2929
if (string.IsNullOrEmpty(sourcePath))
3030
{
3131
throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
@@ -37,6 +37,7 @@ public static void Attach(
3737
}
3838

3939
// Start Angular CLI and attach to middleware pipeline
40+
var appBuilder = spaBuilder.ApplicationBuilder;
4041
var logger = GetOrCreateLogger(appBuilder);
4142
var angularCliServerInfoTask = StartAngularCliServerAsync(sourcePath, npmScriptName, logger);
4243

@@ -48,7 +49,7 @@ public static void Attach(
4849
var targetUriTask = angularCliServerInfoTask.ContinueWith(
4950
task => new UriBuilder("http", "localhost", task.Result.Port).Uri);
5051

51-
SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(appBuilder, targetUriTask);
52+
SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, targetUriTask);
5253
}
5354

5455
internal static ILogger GetOrCreateLogger(IApplicationBuilder appBuilder)

src/Microsoft.AspNetCore.SpaServices.Extensions/AngularCli/AngularCliMiddlewareExtensions.cs

+7-11
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,25 @@ public static class AngularCliMiddlewareExtensions
1919
/// This feature should only be used in development. For production deployments, be
2020
/// sure not to enable the Angular CLI server.
2121
/// </summary>
22-
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
22+
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
2323
/// <param name="npmScript">The name of the script in your package.json file that launches the Angular CLI process.</param>
2424
public static void UseAngularCliServer(
25-
this IApplicationBuilder app,
25+
this ISpaBuilder spaBuilder,
2626
string npmScript)
2727
{
28-
if (app == null)
28+
if (spaBuilder == null)
2929
{
30-
throw new ArgumentNullException(nameof(app));
30+
throw new ArgumentNullException(nameof(spaBuilder));
3131
}
3232

33-
var spaOptions = DefaultSpaOptions.FindInPipeline(app);
34-
if (spaOptions == null)
35-
{
36-
throw new InvalidOperationException($"{nameof(UseAngularCliServer)} should be called inside the 'configure' callback of a call to {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
37-
}
33+
var spaOptions = spaBuilder.Options;
3834

3935
if (string.IsNullOrEmpty(spaOptions.SourcePath))
4036
{
41-
throw new InvalidOperationException($"To use {nameof(UseAngularCliServer)}, you must supply a non-empty value for the {nameof(ISpaOptions.SourcePath)} property of {nameof(ISpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
37+
throw new InvalidOperationException($"To use {nameof(UseAngularCliServer)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
4238
}
4339

44-
AngularCliMiddleware.Attach(app, spaOptions.SourcePath, npmScript);
40+
AngularCliMiddleware.Attach(spaBuilder, npmScript);
4541
}
4642
}
4743
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNetCore.Builder;
5+
6+
namespace Microsoft.AspNetCore.SpaServices
7+
{
8+
internal class DefaultSpaBuilder : ISpaBuilder
9+
{
10+
public IApplicationBuilder ApplicationBuilder { get; }
11+
12+
public SpaOptions Options { get; }
13+
14+
public DefaultSpaBuilder(IApplicationBuilder applicationBuilder, string sourcePath, string urlPrefix)
15+
{
16+
ApplicationBuilder = applicationBuilder
17+
?? throw new System.ArgumentNullException(nameof(applicationBuilder));
18+
19+
Options = new SpaOptions(sourcePath, urlPrefix);
20+
}
21+
}
22+
}

src/Microsoft.AspNetCore.SpaServices.Extensions/DefaultSpaOptions.cs

-51
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNetCore.Builder;
5+
6+
namespace Microsoft.AspNetCore.SpaServices
7+
{
8+
/// <summary>
9+
/// Defines a class that provides mechanisms for configuring the hosting
10+
/// of a Single Page Application (SPA) and attaching middleware.
11+
/// </summary>
12+
public interface ISpaBuilder
13+
{
14+
/// <summary>
15+
/// The <see cref="IApplicationBuilder"/> representing the middleware pipeline
16+
/// in which the SPA is being hosted.
17+
/// </summary>
18+
IApplicationBuilder ApplicationBuilder { get; }
19+
20+
/// <summary>
21+
/// Describes configuration options for hosting a SPA.
22+
/// </summary>
23+
SpaOptions Options { get; }
24+
}
25+
}

src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/ISpaPrerendererBuilder.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ public interface ISpaPrerendererBuilder
1818
/// exists on disk. Prerendering middleware can then execute that file in
1919
/// a Node environment.
2020
/// </summary>
21-
/// <param name="appBuilder">The <see cref="IApplicationBuilder"/>.</param>
21+
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
2222
/// <returns>A <see cref="Task"/> representing completion of the build process.</returns>
23-
Task Build(IApplicationBuilder appBuilder);
23+
Task Build(ISpaBuilder spaBuilder);
2424
}
2525
}

src/Microsoft.AspNetCore.SpaServices.Extensions/Prerendering/SpaPrerenderingExtensions.cs

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33

44
using Microsoft.AspNetCore.Hosting;
55
using Microsoft.AspNetCore.Http;
6-
using Microsoft.AspNetCore.Http.Extensions;
76
using Microsoft.AspNetCore.Http.Features;
87
using Microsoft.AspNetCore.NodeServices;
8+
using Microsoft.AspNetCore.SpaServices;
99
using Microsoft.AspNetCore.SpaServices.Prerendering;
1010
using Microsoft.Extensions.DependencyInjection;
1111
using Microsoft.Net.Http.Headers;
@@ -26,21 +26,21 @@ public static class SpaPrerenderingExtensions
2626
/// <summary>
2727
/// Enables server-side prerendering middleware for a Single Page Application.
2828
/// </summary>
29-
/// <param name="applicationBuilder">The <see cref="IApplicationBuilder"/>.</param>
29+
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
3030
/// <param name="entryPoint">The path, relative to your application root, of the JavaScript file containing prerendering logic.</param>
3131
/// <param name="buildOnDemand">Optional. If specified, executes the supplied <see cref="ISpaPrerendererBuilder"/> before looking for the <paramref name="entryPoint"/> file. This is only intended to be used during development.</param>
3232
/// <param name="excludeUrls">Optional. If specified, requests within these URL paths will bypass the prerenderer.</param>
3333
/// <param name="supplyData">Optional. If specified, this callback will be invoked during prerendering, allowing you to pass additional data to the prerendering entrypoint code.</param>
3434
public static void UseSpaPrerendering(
35-
this IApplicationBuilder applicationBuilder,
35+
this ISpaBuilder spaBuilder,
3636
string entryPoint,
3737
ISpaPrerendererBuilder buildOnDemand = null,
3838
string[] excludeUrls = null,
3939
Action<HttpContext, IDictionary<string, object>> supplyData = null)
4040
{
41-
if (applicationBuilder == null)
41+
if (spaBuilder == null)
4242
{
43-
throw new ArgumentNullException(nameof(applicationBuilder));
43+
throw new ArgumentNullException(nameof(spaBuilder));
4444
}
4545

4646
if (string.IsNullOrEmpty(entryPoint))
@@ -49,9 +49,10 @@ public static void UseSpaPrerendering(
4949
}
5050

5151
// If we're building on demand, start that process in the background now
52-
var buildOnDemandTask = buildOnDemand?.Build(applicationBuilder);
52+
var buildOnDemandTask = buildOnDemand?.Build(spaBuilder);
5353

5454
// Get all the necessary context info that will be used for each prerendering call
55+
var applicationBuilder = spaBuilder.ApplicationBuilder;
5556
var serviceProvider = applicationBuilder.ApplicationServices;
5657
var nodeServices = GetNodeServices(serviceProvider);
5758
var applicationStoppingToken = serviceProvider.GetRequiredService<IApplicationLifetime>()

src/Microsoft.AspNetCore.SpaServices.Extensions/Proxying/SpaProxyingExtensions.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using Microsoft.AspNetCore.Hosting;
5+
using Microsoft.AspNetCore.SpaServices;
56
using Microsoft.AspNetCore.SpaServices.Extensions.Proxy;
67
using System;
78
using System.Threading;
@@ -20,14 +21,14 @@ public static class SpaProxyingExtensions
2021
/// Application (SPA) development server. This is only intended to be used during
2122
/// development. Do not enable this middleware in production applications.
2223
/// </summary>
23-
/// <param name="applicationBuilder">The <see cref="IApplicationBuilder"/>.</param>
24+
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
2425
/// <param name="baseUri">The target base URI to which requests should be proxied.</param>
2526
public static void UseProxyToSpaDevelopmentServer(
26-
this IApplicationBuilder applicationBuilder,
27+
this ISpaBuilder spaBuilder,
2728
Uri baseUri)
2829
{
2930
UseProxyToSpaDevelopmentServer(
30-
applicationBuilder,
31+
spaBuilder,
3132
Task.FromResult(baseUri));
3233
}
3334

@@ -36,12 +37,13 @@ public static void UseProxyToSpaDevelopmentServer(
3637
/// Application (SPA) development server. This is only intended to be used during
3738
/// development. Do not enable this middleware in production applications.
3839
/// </summary>
39-
/// <param name="applicationBuilder">The <see cref="IApplicationBuilder"/>.</param>
40+
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
4041
/// <param name="baseUriTask">A <see cref="Task"/> that resolves with the target base URI to which requests should be proxied.</param>
4142
public static void UseProxyToSpaDevelopmentServer(
42-
this IApplicationBuilder applicationBuilder,
43+
this ISpaBuilder spaBuilder,
4344
Task<Uri> baseUriTask)
4445
{
46+
var applicationBuilder = spaBuilder.ApplicationBuilder;
4547
var applicationStoppingToken = GetStoppingToken(applicationBuilder);
4648

4749
// It's important not to time out the requests, as some of them might be to

src/Microsoft.AspNetCore.SpaServices.Extensions/SpaApplicationBuilderExtensions.cs

+5-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public static class SpaApplicationBuilderExtensions
4040
/// of the default page that hosts your SPA user interface.
4141
/// If not specified, the default value is <c>"index.html"</c>.
4242
/// </param>
43-
/// <param name="configure">
43+
/// <param name="configuration">
4444
/// Optional. If specified, this callback will be invoked so that additional middleware
4545
/// can be registered within the context of this SPA.
4646
/// </param>
@@ -49,16 +49,15 @@ public static void UseSpa(
4949
string urlPrefix,
5050
string sourcePath = null,
5151
string defaultPage = null,
52-
Action<ISpaOptions> configure = null)
52+
Action<ISpaBuilder> configuration = null)
5353
{
54-
var spaOptions = new DefaultSpaOptions(sourcePath, urlPrefix);
55-
spaOptions.RegisterSoleInstanceInPipeline(app);
54+
var spaBuilder = new DefaultSpaBuilder(app, sourcePath, urlPrefix);
5655

5756
// Invoke 'configure' to give the developer a chance to insert extra
5857
// middleware before the 'default page' pipeline entries
59-
configure?.Invoke(spaOptions);
58+
configuration?.Invoke(spaBuilder);
6059

61-
SpaDefaultPageMiddleware.Attach(app, spaOptions);
60+
SpaDefaultPageMiddleware.Attach(spaBuilder);
6261
}
6362
}
6463
}

src/Microsoft.AspNetCore.SpaServices.Extensions/SpaDefaultPageMiddleware.cs

+7-10
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,16 @@ namespace Microsoft.AspNetCore.SpaServices
1010
{
1111
internal class SpaDefaultPageMiddleware
1212
{
13-
public static void Attach(IApplicationBuilder app, ISpaOptions spaOptions)
13+
public static void Attach(ISpaBuilder spaBuilder)
1414
{
15-
if (app == null)
15+
if (spaBuilder == null)
1616
{
17-
throw new ArgumentNullException(nameof(app));
17+
throw new ArgumentNullException(nameof(spaBuilder));
1818
}
1919

20-
if (spaOptions == null)
21-
{
22-
throw new ArgumentNullException(nameof(spaOptions));
23-
}
24-
25-
var defaultPageUrl = ConstructDefaultPageUrl(spaOptions.UrlPrefix, spaOptions.DefaultPage);
20+
var app = spaBuilder.ApplicationBuilder;
21+
var options = spaBuilder.Options;
22+
var defaultPageUrl = ConstructDefaultPageUrl(options.UrlPrefix, options.DefaultPage);
2623

2724
// Rewrite all requests to the default page
2825
app.Use((context, next) =>
@@ -61,7 +58,7 @@ private static string ConstructDefaultPageUrl(string urlPrefix, string defaultPa
6158
{
6259
if (string.IsNullOrEmpty(defaultPage))
6360
{
64-
defaultPage = DefaultSpaOptions.DefaultDefaultPageValue;
61+
defaultPage = SpaOptions.DefaultDefaultPageValue;
6562
}
6663

6764
return new PathString(urlPrefix).Add(new PathString("/" + defaultPage));

0 commit comments

Comments
 (0)