Skip to content

Commit 377ac95

Browse files
committed
Split IApplicationBuilder extensions into separate class, and ensure app.AddReact (registering depenencies in DI container) is called before app.UseReact (registering React middleware)
1 parent 2939af6 commit 377ac95

File tree

8 files changed

+191
-53
lines changed

8 files changed

+191
-53
lines changed

src/React.AspNet/HtmlHelperExtensions.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
* of patent rights can be found in the PATENTS file in the same directory.
88
*/
99

10+
using React.Exceptions;
11+
using React.TinyIoC;
12+
1013
#if LEGACYASPNET
1114
using System.Web;
1215
using System.Web.Mvc;
@@ -33,8 +36,25 @@ public static class HtmlHelperExtensions
3336
/// </summary>
3437
private static IReactEnvironment Environment
3538
{
36-
// TODO: Figure out if this can be injected
37-
get { return global::React.AssemblyRegistration.Container.Resolve<IReactEnvironment>(); }
39+
get
40+
{
41+
try
42+
{
43+
return global::React.AssemblyRegistration.Container.Resolve<IReactEnvironment>();
44+
}
45+
catch (TinyIoCResolutionException ex)
46+
{
47+
throw new ReactNotInitialisedException(
48+
#if LEGACYASPNET
49+
"ReactJS.NET has not been initialised correctly.",
50+
#else
51+
"ReactJS.NET has not been initialised correctly. Please ensure you have " +
52+
"called app.AddReact() and app.UseReact() in your Startup.cs file.",
53+
#endif
54+
ex
55+
);
56+
}
57+
}
3858
}
3959

4060
/// <summary>

src/React.AspNet/HttpContextLifetimeProvider.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Microsoft.AspNet.Hosting;
1414
using Microsoft.AspNet.Http;
1515
using Microsoft.Framework.DependencyInjection;
16+
using React.Exceptions;
1617
using React.TinyIoC;
1718

1819
namespace React.AspNet
@@ -52,8 +53,21 @@ public HttpContextLifetimeProvider(IServiceProvider appServiceProvider)
5253
/// <summary>
5354
/// Gets the current per-request registrations for the current request.
5455
/// </summary>
55-
private PerRequestRegistrations Registrations =>
56-
HttpContext.RequestServices.GetRequiredService<PerRequestRegistrations>();
56+
private PerRequestRegistrations Registrations
57+
{
58+
get
59+
{
60+
var registrations = HttpContext.RequestServices.GetService<PerRequestRegistrations>();
61+
if (registrations == null)
62+
{
63+
throw new ReactNotInitialisedException(
64+
"ReactJS.NET has not been initialised correctly. Please ensure you have " +
65+
"called app.AddReact() and app.UseReact() in your Startup.cs file."
66+
);
67+
}
68+
return registrations;
69+
}
70+
}
5771

5872
/// <summary>
5973
/// Gets the value of this item in the dependency injection container.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2015, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
using System;
11+
using Microsoft.AspNet.Builder;
12+
using Microsoft.AspNet.Hosting;
13+
using Microsoft.Framework.DependencyInjection;
14+
using Microsoft.Framework.Runtime;
15+
using React.Exceptions;
16+
using React.TinyIoC;
17+
18+
namespace React.AspNet
19+
{
20+
/// <summary>
21+
/// Handles registering ReactJS.NET middleware in an ASP.NET <see cref="IApplicationBuilder"/>.
22+
/// </summary>
23+
public static class ReactBuilderExtensions
24+
{
25+
/// <summary>
26+
/// Initialises ReactJS.NET for this application
27+
/// </summary>
28+
/// <param name="app">ASP.NET application builder</param>
29+
/// <param name="configure">ReactJS.NET configuration</param>
30+
/// <param name="fileOptions">Options to use for serving JSX files</param>
31+
/// <returns>The application builder (for chaining)</returns>
32+
public static IApplicationBuilder UseReact(
33+
this IApplicationBuilder app,
34+
Action<IReactSiteConfiguration> configure,
35+
JsxFileOptions fileOptions = null
36+
)
37+
{
38+
EnsureServicesRegistered(app);
39+
40+
// Register IApplicationEnvironment in our dependency injection container
41+
// Ideally this would be in AddReact(IServiceCollection) but we can't
42+
// access IApplicationEnvironment there.
43+
React.AssemblyRegistration.Container.Register(app.ApplicationServices.GetRequiredService<IApplicationEnvironment>());
44+
React.AssemblyRegistration.Container.Register(app.ApplicationServices.GetRequiredService<IHostingEnvironment>());
45+
46+
Initializer.Initialize(registerOptions => AsPerRequestSingleton(app.ApplicationServices, registerOptions));
47+
configure(ReactSiteConfiguration.Configuration);
48+
49+
// Allow serving of .jsx files
50+
app.UseMiddleware<JsxFileMiddleware>(fileOptions ?? new JsxFileOptions());
51+
52+
return app;
53+
}
54+
55+
/// <summary>
56+
/// Registers a class such that every ASP.NET web request has a single instance of it.
57+
/// </summary>
58+
/// <param name="appServiceProvider">ASP.NET service provider</param>
59+
/// <param name="registerOptions">Registration options</param>
60+
/// <returns>Registration options (for chaining)</returns>
61+
private static TinyIoCContainer.RegisterOptions AsPerRequestSingleton(
62+
IServiceProvider appServiceProvider,
63+
TinyIoCContainer.RegisterOptions registerOptions
64+
)
65+
{
66+
return TinyIoCContainer.RegisterOptions.ToCustomLifetimeManager(
67+
registerOptions,
68+
new HttpContextLifetimeProvider(appServiceProvider),
69+
"per request singleton"
70+
);
71+
}
72+
73+
/// <summary>
74+
/// Ensures React services have been registered in the ASP.NET dependency injection container.
75+
/// </summary>
76+
/// <param name="app">ASP.NET application builder</param>
77+
private static void EnsureServicesRegistered(IApplicationBuilder app)
78+
{
79+
var registrations = app.ApplicationServices.GetService<HttpContextLifetimeProvider.PerRequestRegistrations>();
80+
if (registrations == null)
81+
{
82+
throw new ReactNotInitialisedException("Please call app.AddReact() before app.UseReact().");
83+
}
84+
}
85+
}
86+
}

src/React.AspNet/ReactServiceCollectionExtensions.cs

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77
* of patent rights can be found in the PATENTS file in the same directory.
88
*/
99

10-
using System;
11-
using Microsoft.AspNet.Builder;
1210
using Microsoft.Framework.DependencyInjection;
13-
using Microsoft.Framework.Runtime;
14-
using React.TinyIoC;
1511

1612
namespace React.AspNet
1713
{
14+
/// <summary>
15+
/// Handles registering ReactJS.NET services in the ASP.NET <see cref="IServiceCollection"/>.
16+
/// </summary>
1817
public static class ReactServiceCollectionExtensions
1918
{
2019
/// <summary>
@@ -27,50 +26,5 @@ public static IServiceCollection AddReact(this IServiceCollection services)
2726
services.AddScoped<HttpContextLifetimeProvider.PerRequestRegistrations>();
2827
return services;
2928
}
30-
31-
/// <summary>
32-
/// Initialises ReactJS.NET for this application
33-
/// </summary>
34-
/// <param name="app">ASP.NET application builder</param>
35-
/// <param name="configure">ReactJS.NET configuration</param>
36-
/// <param name="fileOptions">Options to use for serving JSX files</param>
37-
/// <returns>The application builder (for chaining)</returns>
38-
public static IApplicationBuilder UseReact(
39-
this IApplicationBuilder app,
40-
Action<IReactSiteConfiguration> configure,
41-
JsxFileOptions fileOptions = null
42-
)
43-
{
44-
// Register IApplicationEnvironment in our dependency injection container
45-
// Ideally this would be in AddReact(IServiceCollection) but we can't
46-
// access IApplicationEnvironment there.
47-
React.AssemblyRegistration.Container.Register(app.ApplicationServices.GetRequiredService<IApplicationEnvironment>());
48-
49-
Initializer.Initialize(registerOptions => AsPerRequestSingleton(app.ApplicationServices, registerOptions));
50-
configure(ReactSiteConfiguration.Configuration);
51-
52-
// Allow serving of .jsx files
53-
app.UseMiddleware<JsxFileMiddleware>(fileOptions ?? new JsxFileOptions());
54-
55-
return app;
56-
}
57-
58-
/// <summary>
59-
/// Registers a class such that every ASP.NET web request has a single instance of it.
60-
/// </summary>
61-
/// <param name="appServiceProvider">ASP.NET service provider</param>
62-
/// <param name="registerOptions">Registration options</param>
63-
/// <returns>Registration options (for chaining)</returns>
64-
private static TinyIoCContainer.RegisterOptions AsPerRequestSingleton(
65-
IServiceProvider appServiceProvider,
66-
TinyIoCContainer.RegisterOptions registerOptions
67-
)
68-
{
69-
return TinyIoCContainer.RegisterOptions.ToCustomLifetimeManager(
70-
registerOptions,
71-
new HttpContextLifetimeProvider(appServiceProvider),
72-
"per request singleton"
73-
);
74-
}
7529
}
7630
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2015, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
using System;
11+
using System.Runtime.Serialization;
12+
13+
namespace React.Exceptions
14+
{
15+
/// <summary>
16+
/// Thrown when React has not been initialised correctly.
17+
/// </summary>
18+
[Serializable]
19+
public class ReactNotInitialisedException : ReactException
20+
{
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="ReactNotInitialisedException"/> class.
23+
/// </summary>
24+
/// <param name="message">The message that describes the error.</param>
25+
public ReactNotInitialisedException(string message) : base(message) { }
26+
27+
/// <summary>
28+
/// Initializes a new instance of the <see cref="ReactNotInitialisedException"/> class.
29+
/// </summary>
30+
/// <param name="message">The error message that explains the reason for the exception.</param>
31+
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
32+
public ReactNotInitialisedException(string message, Exception innerException)
33+
: base(message, innerException) { }
34+
35+
/// <summary>
36+
/// Used by deserialization
37+
/// </summary>
38+
protected ReactNotInitialisedException(SerializationInfo info, StreamingContext context)
39+
: base(info, context) { }
40+
}
41+
}

src/React.Core/React.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<Link>Properties\SharedAssemblyVersionInfo.cs</Link>
9090
</Compile>
9191
<Compile Include="AssemblyRegistration.cs" />
92+
<Compile Include="Exceptions\ReactNotInitialisedException.cs" />
9293
<Compile Include="JavaScriptEngineUtils.cs" />
9394
<Compile Include="VroomJsEngine.cs" />
9495
<Compile Include="Exceptions\ClearScriptV8InitialisationException.cs" />

src/React.Sample.Mvc6/project.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"Microsoft.Framework.Logging.Console": "1.0.0.0-beta3",
1515
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta1",
1616
"React.AspNet": "",
17+
"React.Core": "",
1718
"JavaScriptEngineSwitcher.V8": "1.2.4.0",
1819
"JavaScriptEngineSwitcher.Msie": "1.2.4.0"
1920
},

src/wrap/React/project.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"version": "1.4.0-*",
3+
"frameworks": {
4+
"net40": {
5+
"wrappedProject": "../../React.Core/React.Core.csproj",
6+
"bin": {
7+
"assembly": "../../React.Core/obj/{configuration}/React.dll",
8+
"pdb": "../../React.Core/obj/{configuration}/React.pdb"
9+
},
10+
"dependencies": {
11+
"JavaScriptEngineSwitcher.Core": "1.2.4",
12+
"JavaScriptEngineSwitcher.V8": "1.2.4",
13+
"JSPool": "0.2.0",
14+
"Newtonsoft.Json": "5.0.4",
15+
"VroomJs": "1.0.0-*",
16+
"JavaScriptEngineSwitcher.Msie": "1.2.4",
17+
"MsieJavaScriptEngine": "1.5.1"
18+
}
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)