Skip to content

Commit 5d48be6

Browse files
committed
Add support for ASP.NET 5
- Created a Visual Studio 2015 solution (React.VS2015.sln) containing two new projects: React.AspNet5 and React.Samples.Mvc6. - Copied MVC 4 example to create a new MVC 6 example. Uses Gulp to build JSX file. - Integrated per-request registration into our TinyIoC for ASP.NET 5.
1 parent 783b4e3 commit 5d48be6

26 files changed

+1050
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ src/React.Sample.*/ClearScript.V8
99
src/React.Sample.Webpack/build
1010
*.generated.js
1111
*.generated.js.map
12+
src/React.Sample.Mvc6/wwwroot/js/Sample.js
1213

1314
## Ignore Visual Studio temporary files, build results, and
1415
## files generated by popular Visual Studio add-ons.

build.proj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ of patent rights can be found in the PATENTS file in the same directory.
3232

3333
<Import Project="$(MSBuildProjectDirectory)\tools\MSBuildTasks\MSBuild.Community.Tasks.Targets" />
3434
<UsingTask
35-
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll"
35+
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll"
3636
TaskName="TransformXml"
3737
/>
3838

dev-build-vs2015.bat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@echo off
2+
"%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" build.proj /p:BuildType=Dev
3+
pause
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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.IO;
11+
using Microsoft.Framework.Runtime;
12+
13+
namespace React.AspNet5
14+
{
15+
/// <summary>
16+
/// Handles file system functionality, such as reading files. Maps all paths from
17+
/// application-relative (~/...) to full paths using ASP.NET's MapPath method.
18+
/// </summary>
19+
public class AspNet5FileSystem : FileSystemBase
20+
{
21+
private const string PREFIX = "~/";
22+
private readonly IApplicationEnvironment _appEnvironment;
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="AspNet5FileSystem"/> class.
26+
/// </summary>
27+
/// <param name="appEnvironment">The ASP.NET 5 application environment</param>
28+
public AspNet5FileSystem(IApplicationEnvironment appEnvironment)
29+
{
30+
_appEnvironment = appEnvironment;
31+
}
32+
33+
/// <summary>
34+
/// Converts a path from an application relative path (~/...) to a full filesystem path
35+
/// </summary>
36+
/// <param name="relativePath">App-relative path of the file</param>
37+
/// <returns>Full path of the file</returns>
38+
public override string MapPath(string relativePath)
39+
{
40+
if (relativePath.StartsWith(PREFIX))
41+
{
42+
relativePath = relativePath.Substring(PREFIX.Length);
43+
}
44+
return Path.Combine(_appEnvironment.ApplicationBasePath, relativePath);
45+
}
46+
}
47+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 React.TinyIoC;
11+
12+
namespace React.AspNet5
13+
{
14+
/// <summary>
15+
/// Handles registration of ReactJS.NET components that are only applicable
16+
/// in the context of an ASP.NET web application.
17+
/// </summary>
18+
public class AssemblyRegistration : IAssemblyRegistration
19+
{
20+
/// <summary>
21+
/// Registers components in the React IoC container
22+
/// </summary>
23+
/// <param name="container">Container to register components in</param>
24+
public void Register(TinyIoCContainer container)
25+
{
26+
container.Register<IFileSystem, AspNet5FileSystem>().AsSingleton();
27+
// TODO: Caching for ASP.NET 5
28+
container.Register<ICache, NullCache>().AsSingleton();
29+
}
30+
}
31+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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 Microsoft.AspNet.Mvc.Rendering;
11+
12+
namespace React.AspNet5
13+
{
14+
/// <summary>
15+
/// HTML Helpers for utilising React from an ASP.NET MVC 6 (vNext) application.
16+
/// </summary>
17+
public static class HtmlHelperExtensions
18+
{
19+
// TODO: Figure out if this can be injected
20+
/// <summary>
21+
/// Gets the React environment
22+
/// </summary>
23+
private static IReactEnvironment Environment =>
24+
global::React.AssemblyRegistration.Container.Resolve<IReactEnvironment>();
25+
26+
/// <summary>
27+
/// Renders the specified React component
28+
/// </summary>
29+
/// <typeparam name="T">Type of the props</typeparam>
30+
/// <param name="htmlHelper">HTML helper</param>
31+
/// <param name="componentName">Name of the component</param>
32+
/// <param name="props">Props to initialise the component with</param>
33+
/// <param name="htmlTag">HTML tag to wrap the component in. Defaults to &lt;div&gt;</param>
34+
/// <param name="containerId">ID to use for the container HTML tag. Defaults to an auto-generated ID</param>
35+
/// <returns>The component's HTML</returns>
36+
public static HtmlString React<T>(
37+
this IHtmlHelper htmlHelper,
38+
string componentName,
39+
T props,
40+
string htmlTag = null,
41+
string containerId = null
42+
)
43+
{
44+
var reactComponent = Environment.CreateComponent(componentName, props, containerId);
45+
if (!string.IsNullOrEmpty(htmlTag))
46+
{
47+
reactComponent.ContainerTag = htmlTag;
48+
}
49+
var result = reactComponent.RenderHtml();
50+
return new HtmlString(result);
51+
}
52+
53+
/// <summary>
54+
/// Renders the specified React component, along with its client-side initialisation code.
55+
/// Normally you would use the <see cref="React{T}"/> method, but <see cref="ReactWithInit{T}"/>
56+
/// is useful when rendering self-contained partial views.
57+
/// </summary>
58+
/// <typeparam name="T">Type of the props</typeparam>
59+
/// <param name="htmlHelper">HTML helper</param>
60+
/// <param name="componentName">Name of the component</param>
61+
/// <param name="props">Props to initialise the component with</param>
62+
/// <param name="htmlTag">HTML tag to wrap the component in. Defaults to &lt;div&gt;</param>
63+
/// <param name="containerId">ID to use for the container HTML tag. Defaults to an auto-generated ID</param>
64+
/// <returns>The component's HTML</returns>
65+
public static HtmlString ReactWithInit<T>(
66+
this IHtmlHelper htmlHelper,
67+
string componentName,
68+
T props,
69+
string htmlTag = null,
70+
string containerId = null
71+
)
72+
{
73+
var reactComponent = Environment.CreateComponent(componentName, props, containerId);
74+
if (!string.IsNullOrEmpty(htmlTag))
75+
{
76+
reactComponent.ContainerTag = htmlTag;
77+
}
78+
var html = reactComponent.RenderHtml();
79+
var script = new TagBuilder("script")
80+
{
81+
InnerHtml = reactComponent.RenderJavaScript()
82+
};
83+
return new HtmlString(html + System.Environment.NewLine + script.ToString());
84+
}
85+
86+
/// <summary>
87+
/// Renders the JavaScript required to initialise all components client-side. This will
88+
/// attach event handlers to the server-rendered HTML.
89+
/// </summary>
90+
/// <returns>JavaScript for all components</returns>
91+
public static HtmlString ReactInitJavaScript(this IHtmlHelper htmlHelper)
92+
{
93+
var script = Environment.GetInitJavaScript();
94+
var tag = new TagBuilder("script")
95+
{
96+
InnerHtml = script
97+
};
98+
return new HtmlString(tag.ToString());
99+
}
100+
}
101+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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.Collections.Concurrent;
12+
using System.Linq;
13+
using Microsoft.AspNet.Http;
14+
using Microsoft.Framework.DependencyInjection;
15+
using React.TinyIoC;
16+
17+
namespace React.AspNet5
18+
{
19+
/// <summary>
20+
/// Handles registering per-request objects in the dependency injection container.
21+
/// </summary>
22+
internal class HttpContextLifetimeProvider : TinyIoCContainer.ITinyIoCObjectLifetimeProvider
23+
{
24+
private readonly IServiceProvider _appServiceProvider;
25+
26+
/// <summary>
27+
/// Creates a new <see cref="HttpContextLifetimeProvider" />.
28+
/// </summary>
29+
/// <param name="appServiceProvider">ASP.NET dependency injection service provider</param>
30+
public HttpContextLifetimeProvider(IServiceProvider appServiceProvider)
31+
{
32+
_appServiceProvider = appServiceProvider;
33+
}
34+
35+
/// <summary>
36+
/// Prefix to use on HttpContext items
37+
/// </summary>
38+
private const string PREFIX = "React.PerRequest.";
39+
40+
/// <summary>
41+
/// Name of the key for this particular registration
42+
/// </summary>
43+
private readonly string _keyName = PREFIX + Guid.NewGuid();
44+
45+
/// <summary>
46+
/// Gets the <see cref="HttpContext" /> of the current request.
47+
/// </summary>
48+
private HttpContext HttpContext =>
49+
_appServiceProvider.GetRequiredService<IContextAccessor<HttpContext>>().Value;
50+
51+
/// <summary>
52+
/// Gets the current per-request registrations for the current request.
53+
/// </summary>
54+
private PerRequestRegistrations Registrations =>
55+
HttpContext.RequestServices.GetRequiredService<PerRequestRegistrations>();
56+
57+
/// <summary>
58+
/// Gets the value of this item in the dependency injection container.
59+
/// </summary>
60+
/// <returns></returns>
61+
public object GetObject()
62+
{
63+
object value;
64+
Registrations.TryGetValue(_keyName, out value);
65+
return value;
66+
}
67+
68+
/// <summary>
69+
/// Sets the value of this item in the dependency injection container.
70+
/// </summary>
71+
/// <param name="value">Value to set</param>
72+
public void SetObject(object value)
73+
{
74+
Registrations[_keyName] = value;
75+
}
76+
77+
/// <summary>
78+
/// Removes this item from the dependency injection container.
79+
/// </summary>
80+
public void ReleaseObject()
81+
{
82+
object value;
83+
if (Registrations.TryRemove(_keyName, out value))
84+
{
85+
if (value is IDisposable)
86+
{
87+
((IDisposable)value).Dispose();
88+
}
89+
}
90+
}
91+
92+
/// <summary>
93+
/// Contains all per-request dependency injection registrations.
94+
/// </summary>
95+
internal class PerRequestRegistrations : ConcurrentDictionary<string, object>, IDisposable
96+
{
97+
/// <summary>
98+
/// Disposes all registrations in this container.
99+
/// </summary>
100+
public void Dispose()
101+
{
102+
foreach (var kvp in this.Where(kvp => kvp.Value is IDisposable))
103+
{
104+
((IDisposable)kvp.Value).Dispose();
105+
}
106+
}
107+
}
108+
}
109+
}

src/React.AspNet5/React.AspNet5.kproj

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
5+
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
6+
</PropertyGroup>
7+
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
8+
<PropertyGroup Label="Globals">
9+
<ProjectGuid>a7acdb56-5e43-40a6-92c9-2c52228e6074</ProjectGuid>
10+
<RootNamespace>React.AspNet5</RootNamespace>
11+
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
12+
<OutputPath Condition="'$(OutputPath)'=='' ">..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
13+
</PropertyGroup>
14+
<PropertyGroup>
15+
<SchemaVersion>2.0</SchemaVersion>
16+
</PropertyGroup>
17+
<ItemGroup>
18+
<ProjectReference Include="..\React\React.csproj" />
19+
</ItemGroup>
20+
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
21+
<ProjectExtensions>
22+
<VisualStudio>
23+
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
24+
</VisualStudio>
25+
</ProjectExtensions>
26+
</Project>

0 commit comments

Comments
 (0)