-
Notifications
You must be signed in to change notification settings - Fork 926
React Router Support #407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React Router Support #407
Changes from 9 commits
4f32fa3
132180b
ac7fd9e
debe9b9
81beb7c
66cdcbd
57f07ed
c04716e
e6f570d
c2514c4
6d14da0
5a233ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,8 +10,8 @@ of patent rights can be found in the PATENTS file in the same directory. | |
<Project ToolsVersion="4.0" DefaultTargets="Build;Test;Package" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<Major>3</Major> | ||
<Minor>0</Minor> | ||
<Build>1</Build> | ||
<Minor>1</Minor> | ||
<Build>0</Build> | ||
<Revision>0</Revision> | ||
<DevNuGetServer>http://reactjs.net/packages/</DevNuGetServer> | ||
<MSBuildCommunityTasksPath>$(MSBuildProjectDirectory)\tools\MSBuildTasks</MSBuildCommunityTasksPath> | ||
|
@@ -26,8 +26,9 @@ of patent rights can be found in the PATENTS file in the same directory. | |
<PackageAssemblies Include="React.Core" /> | ||
<PackageAssemblies Include="React.MSBuild" /> | ||
<PackageAssemblies Include="React.Owin" /> | ||
<PackageAssemblies Include="React.Web" /> | ||
<PackageAssemblies Include="React.Web.Mvc4" /> | ||
<PackageAssemblies Include="React.Router" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation looks a bit off here, maybe it's using spaces instead of tabs? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whoops, i went over the .cs files and tabified but i missed this file and some xml. fixed |
||
<PackageAssemblies Include="React.Web" /> | ||
<PackageAssemblies Include="React.Web.Mvc4" /> | ||
<PackageAssemblies Include="System.Web.Optimization.React" /> | ||
</ItemGroup> | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<configuration> | ||
<system.web.webPages.razor> | ||
<pages> | ||
<namespaces> | ||
<add namespace="React.Router" /> | ||
</namespaces> | ||
</pages> | ||
</system.web.webPages.razor> | ||
</configuration> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The react router htmlhelper lives in this namespace. I seem to recall being unable to use it if I didn't either have this statement in my web.config or an explicit using statement in file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, interesting. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright (c) 2014-Present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
namespace React.Router | ||
{ | ||
/// <summary> | ||
/// Contains the context object used during execution in addition to | ||
/// the string result of rendering the React Router component. | ||
/// </summary> | ||
public class ExecutionResult | ||
{ | ||
/// <summary> | ||
/// String result of ReactDOMServer render of provided component. | ||
/// </summary> | ||
public string RenderResult { get; set; } | ||
|
||
/// <summary> | ||
/// Context object used during JS engine execution. | ||
/// </summary> | ||
public RoutingContext Context { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/* | ||
* Copyright (c) 2014-Present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
using System; | ||
using React.Exceptions; | ||
using React.TinyIoC; | ||
|
||
#if NET451 | ||
using System.Web; | ||
using System.Web.Mvc; | ||
using HttpResponse = System.Web.HttpResponseBase; | ||
using IHtmlHelper = System.Web.Mvc.HtmlHelper; | ||
#else | ||
using Microsoft.AspNetCore.Mvc.Rendering; | ||
using IHtmlString = Microsoft.AspNetCore.Html.IHtmlContent; | ||
using HttpResponse = Microsoft.AspNetCore.Http.HttpResponse; | ||
using Microsoft.AspNetCore.Html; | ||
#endif | ||
|
||
namespace React.Router | ||
{ | ||
/// <summary> | ||
/// Render a React StaticRouter Component with context. | ||
/// </summary> | ||
public static class HtmlHelperExtensions | ||
{ | ||
/// <summary> | ||
/// Gets the React environment | ||
/// </summary> | ||
private static IReactEnvironment Environment | ||
{ | ||
get | ||
{ | ||
try | ||
{ | ||
return ReactEnvironment.Current; | ||
} | ||
catch (TinyIoCResolutionException ex) | ||
{ | ||
throw new ReactNotInitialisedException( | ||
#if NET451 | ||
"ReactJS.NET has not been initialised correctly.", | ||
#else | ||
"ReactJS.NET has not been initialised correctly. Please ensure you have " + | ||
"called services.AddReact() and app.UseReact() in your Startup.cs file.", | ||
#endif | ||
ex | ||
); | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can probably just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, that particular block is copied from react.aspnet\htmlhelperextensions.cs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case, I wanted to show a more specific error message for if ReactJS.NET was misconfigured. I guess that's useful here too though! Maybe |
||
|
||
/// <summary> | ||
/// Render a React StaticRouter Component with context object. | ||
/// Can optionally be provided with a custom context handler to handle the various status codes. | ||
/// | ||
/// </summary> | ||
/// <param name="htmlHelper">MVC Razor <see cref="IHtmlHelper"/></param> | ||
/// <param name="componentName">Name of React Static Router component. Expose component globally to ReactJS.NET</param> | ||
/// <param name="props">Props to initialise the component with</param> | ||
/// <param name="path">F.x. from Request.Path. Used by React Static Router to determine context and routing.</param> | ||
/// <param name="Response">Used either by contextHandler or internally to modify the Response status code and redirect.</param> | ||
/// <param name="contextHandler">Optional custom context handler, can be used instead of providing a Response object</param> | ||
/// <param name="htmlTag">HTML tag to wrap the component in. Defaults to <div></param> | ||
/// <param name="containerId">ID to use for the container HTML tag. Defaults to an auto-generated ID</param> | ||
/// <param name="clientOnly">Skip rendering server-side and only output client-side initialisation code. Defaults to <c>false</c></param> | ||
/// <param name="serverOnly">Skip rendering React specific data-attributes during server side rendering. Defaults to <c>false</c></param> | ||
/// <param name="containerClass">HTML class(es) to set on the container tag</param> | ||
/// <returns><see cref="IHtmlString"/> containing the rendered markup for provided React Router component</returns> | ||
public static IHtmlString ReactRouterWithContext<T>( | ||
this IHtmlHelper htmlHelper, | ||
string componentName, | ||
T props, | ||
string path = null, | ||
HttpResponse Response = null, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
string htmlTag = null, | ||
string containerId = null, | ||
bool clientOnly = false, | ||
bool serverOnly = false, | ||
string containerClass = null, | ||
Action<HttpResponse, RoutingContext> contextHandler = null | ||
) | ||
{ | ||
try | ||
{ | ||
path = path ?? htmlHelper.ViewContext.HttpContext.Request.Path; | ||
Response = Response ?? htmlHelper.ViewContext.HttpContext.Response; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you actually need the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, this was just intended as a convenience and i was hoping it might help for unit testing? But I guess it might just be confusing, I'll remove it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah i'm mocking the response obj in the unit tests, should I be doing this a different way? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For unit testing, you should be able to mock the |
||
|
||
var reactComponent | ||
= Environment.CreateRouterComponent | ||
(componentName, props, path, containerId, clientOnly); | ||
|
||
if (!string.IsNullOrEmpty(htmlTag)) | ||
{ | ||
reactComponent.ContainerTag = htmlTag; | ||
} | ||
if (!string.IsNullOrEmpty(containerClass)) | ||
{ | ||
reactComponent.ContainerClass = containerClass; | ||
} | ||
|
||
var executionResult = reactComponent.RenderRouterWithContext(clientOnly, serverOnly); | ||
|
||
if (executionResult.Context?.status != null) | ||
{ | ||
// Use provided contextHandler | ||
if (contextHandler != null) | ||
{ | ||
contextHandler(Response, executionResult.Context); | ||
} | ||
// Handle routing context internally | ||
else | ||
{ | ||
HandleRoutingContext(executionResult.Context, Response); | ||
} | ||
} | ||
|
||
return new HtmlString(executionResult.RenderResult); | ||
} | ||
finally | ||
{ | ||
Environment.ReturnEngineToPool(); | ||
} | ||
} | ||
|
||
private static void HandleRoutingContext(RoutingContext context, HttpResponse Response) | ||
{ | ||
var statusCode = context.status.Value; | ||
|
||
// 300-399 | ||
if (statusCode >= 300 && statusCode < 400) | ||
{ | ||
if (!string.IsNullOrEmpty(context.url)) | ||
{ | ||
if (statusCode == 301) | ||
{ | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the blank line please |
||
#if NET451 | ||
|
||
Response.RedirectPermanent(context.url); | ||
#else | ||
Response.Redirect(context.url, true); | ||
#endif | ||
} | ||
else // 302 and all others | ||
{ | ||
Response.Redirect(context.url); | ||
} | ||
} | ||
else | ||
{ | ||
throw new ReactRouterException("Router requested redirect but no url provided."); | ||
} | ||
} | ||
else | ||
{ | ||
Response.StatusCode = statusCode; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It feels a bit strange for a HTML helper to set the status code, I can't think of a better way to do this though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, moved to helper class |
||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
using System.Reflection; | ||
using System.Runtime.InteropServices; | ||
|
||
[assembly: AssemblyTitle("React.Router")] | ||
[assembly: AssemblyDescription("React Router support for ReactJS.NET")] | ||
[assembly: ComVisible(false)] | ||
[assembly: Guid("277850fc-8765-4042-945f-a50b8f2525a9")] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<Description>React Router support for ReactJS.NET.</Description> | ||
<Copyright>Copyright 2014-Present Facebook, Inc</Copyright> | ||
<AssemblyTitle>ReactJS.NET Router</AssemblyTitle> | ||
<Authors>Daniel Lo Nigro, Gunnar Már Óttarsson</Authors> | ||
<TargetFrameworks>net451;netstandard1.6</TargetFrameworks> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<AssemblyName>React.Router</AssemblyName> | ||
<AssemblyOriginatorKeyFile>../key.snk</AssemblyOriginatorKeyFile> | ||
<SignAssembly>true</SignAssembly> | ||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> | ||
<PackageId>React.Router</PackageId> | ||
<PackageTags>asp.net;mvc;asp;javascript;js;react;facebook;reactjs;babel</PackageTags> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add |
||
<PackageIconUrl>http://reactjs.net/img/logo_64.png</PackageIconUrl> | ||
<PackageProjectUrl>http://reactjs.net/</PackageProjectUrl> | ||
<PackageLicenseUrl>https://github.com/reactjs/React.NET#licence</PackageLicenseUrl> | ||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net451|AnyCPU'"> | ||
<DefineConstants>TRACE;DEBUG;ASPNETCORE;NET451</DefineConstants> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="..\SharedAssemblyInfo.cs" /> | ||
<Compile Include="..\SharedAssemblyVersionInfo.cs" /> | ||
<Content Include="Content\**\*"> | ||
<Pack>true</Pack> | ||
<PackagePath>content\</PackagePath> | ||
</Content> | ||
</ItemGroup> | ||
|
||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.6' "> | ||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="1.0.3" /> | ||
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="1.0.3" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' "> | ||
<Reference Include="System" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can remove this, |
||
<Reference Include="System.Web" /> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<PackageReference Include="Microsoft.AspNet.Mvc" Version="4.0.20710" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\React.Core\React.Core.csproj" /> | ||
</ItemGroup> | ||
|
||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> | ||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||
</PropertyGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright (c) 2014-Present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
namespace React.Router | ||
{ | ||
/// <summary> | ||
/// <see cref="ReactEnvironment"/> extension for rendering a React Router Component with context | ||
/// </summary> | ||
public static class ReactEnvironmentExtensions | ||
{ | ||
/// <summary> | ||
/// Create a React Router Component with context and add it to the list of components to render client side, | ||
/// if applicable. | ||
/// </summary> | ||
/// <typeparam name="T">Type of the props</typeparam> | ||
/// <param name="env">React Environment</param> | ||
/// <param name="componentName">Name of the component</param> | ||
/// <param name="props">Props to use</param> | ||
/// <param name="path">F.x. from Request.Path. Used by React Static Router to determine context and routing.</param> | ||
/// <param name="containerId">ID to use for the container HTML tag. Defaults to an auto-generated ID</param> | ||
/// <param name="clientOnly">True if server-side rendering will be bypassed. Defaults to false.</param> | ||
/// <returns></returns> | ||
public static ReactRouterComponent CreateRouterComponent<T>( | ||
this IReactEnvironment env, | ||
string componentName, | ||
T props, | ||
string path, | ||
string containerId = null, | ||
bool clientOnly = false | ||
) | ||
{ | ||
var config = AssemblyRegistration.Container.Resolve<IReactSiteConfiguration>(); | ||
|
||
var component = new ReactRouterComponent( | ||
env, | ||
config, | ||
componentName, | ||
containerId, | ||
path | ||
) | ||
{ | ||
Props = props, | ||
}; | ||
|
||
env.CreateComponent(component, clientOnly); | ||
return component; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't worry about bumping this, I'll bump it on release.