From c72d656965af6b5fc4748a6a33e38713a7ac25b9 Mon Sep 17 00:00:00 2001 From: Daniil Sokolyuk Date: Sun, 8 Apr 2018 18:48:13 +0300 Subject: [PATCH] Native IHtmlContent/IHtmlString --- src/React.AspNet/ActionHtmlString.cs | 64 +++++++++ src/React.AspNet/HtmlHelperExtensions.cs | 124 ++++++++---------- src/React.Web.Mvc4/React.Web.Mvc4.csproj | 2 +- .../Mvc/HtmlHelperExtensionsTests.cs | 12 +- 4 files changed, 125 insertions(+), 77 deletions(-) create mode 100644 src/React.AspNet/ActionHtmlString.cs diff --git a/src/React.AspNet/ActionHtmlString.cs b/src/React.AspNet/ActionHtmlString.cs new file mode 100644 index 000000000..a2879dde2 --- /dev/null +++ b/src/React.AspNet/ActionHtmlString.cs @@ -0,0 +1,64 @@ +/* + * 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 System.IO; + +#if LEGACYASPNET +using System.Web; +#else +using System.Text.Encodings.Web; +using IHtmlString = Microsoft.AspNetCore.Html.IHtmlContent; +#endif + +#if LEGACYASPNET +namespace React.Web.Mvc +#else +namespace React.AspNet +#endif +{ + /// + /// IHtmlString or IHtmlString action wrapper implementation + /// + public class ActionHtmlString : IHtmlString + { + private readonly Action _textWriter; + + /// + /// Constructor IHtmlString or IHtmlString action wrapper implementation + /// + /// + public ActionHtmlString(Action textWriter) + { + _textWriter = textWriter; + } + +#if LEGACYASPNET + /// Returns an HTML-encoded string. + /// An HTML-encoded string. + public string ToHtmlString() + { + var sw = new StringWriter(); + _textWriter(sw); + return sw.ToString(); + } +#else + /// + /// Writes the content by encoding it with the specified + /// to the specified . + /// + /// The to which the content is written. + /// The which encodes the content to be written. + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + _textWriter(writer); + } +#endif + } +} diff --git a/src/React.AspNet/HtmlHelperExtensions.cs b/src/React.AspNet/HtmlHelperExtensions.cs index a3d796eb2..cd821cd48 100644 --- a/src/React.AspNet/HtmlHelperExtensions.cs +++ b/src/React.AspNet/HtmlHelperExtensions.cs @@ -12,13 +12,10 @@ #if LEGACYASPNET using System.Web; -using System.Web.Mvc; using IHtmlHelper = System.Web.Mvc.HtmlHelper; #else -using System.Text.Encodings.Web; using Microsoft.AspNetCore.Mvc.Rendering; using IHtmlString = Microsoft.AspNetCore.Html.IHtmlContent; -using Microsoft.AspNetCore.Html; #endif #if LEGACYASPNET @@ -32,7 +29,6 @@ namespace React.AspNet /// public static class HtmlHelperExtensions { - /// /// Gets the React environment /// @@ -70,24 +66,28 @@ public static IHtmlString React( Action exceptionHandler = null ) { - try + return new ActionHtmlString(writer => { - var reactComponent = Environment.CreateComponent(componentName, props, containerId, clientOnly, serverOnly); - if (!string.IsNullOrEmpty(htmlTag)) + try { - reactComponent.ContainerTag = htmlTag; + var reactComponent = Environment.CreateComponent(componentName, props, containerId, clientOnly, serverOnly); + if (!string.IsNullOrEmpty(htmlTag)) + { + reactComponent.ContainerTag = htmlTag; + } + + if (!string.IsNullOrEmpty(containerClass)) + { + reactComponent.ContainerClass = containerClass; + } + + writer.Write(reactComponent.RenderHtml(clientOnly, serverOnly, exceptionHandler)); } - if (!string.IsNullOrEmpty(containerClass)) + finally { - reactComponent.ContainerClass = containerClass; + Environment.ReturnEngineToPool(); } - var result = reactComponent.RenderHtml(clientOnly, serverOnly, exceptionHandler); - return new HtmlString(result); - } - finally - { - Environment.ReturnEngineToPool(); - } + }); } /// @@ -116,25 +116,30 @@ public static IHtmlString ReactWithInit( Action exceptionHandler = null ) { - try + return new ActionHtmlString(writer => { - var reactComponent = Environment.CreateComponent(componentName, props, containerId, clientOnly); - if (!string.IsNullOrEmpty(htmlTag)) + try { - reactComponent.ContainerTag = htmlTag; + var reactComponent = Environment.CreateComponent(componentName, props, containerId, clientOnly); + if (!string.IsNullOrEmpty(htmlTag)) + { + reactComponent.ContainerTag = htmlTag; + } + + if (!string.IsNullOrEmpty(containerClass)) + { + reactComponent.ContainerClass = containerClass; + } + + writer.Write(reactComponent.RenderHtml(clientOnly, exceptionHandler: exceptionHandler)); + writer.WriteLine(); + WriteScriptTag(writer, bodyWriter => bodyWriter.Write(reactComponent.RenderJavaScript())); } - if (!string.IsNullOrEmpty(containerClass)) + finally { - reactComponent.ContainerClass = containerClass; + Environment.ReturnEngineToPool(); } - var html = reactComponent.RenderHtml(clientOnly, exceptionHandler: exceptionHandler); - - return new HtmlString(html + System.Environment.NewLine + RenderToString(GetScriptTag(reactComponent.RenderJavaScript()))); - } - finally - { - Environment.ReturnEngineToPool(); - } + }); } /// @@ -144,55 +149,34 @@ public static IHtmlString ReactWithInit( /// JavaScript for all components public static IHtmlString ReactInitJavaScript(this IHtmlHelper htmlHelper, bool clientOnly = false) { - try - { - return GetScriptTag(Environment.GetInitJavaScript(clientOnly)); - } - finally + return new ActionHtmlString(writer => { - Environment.ReturnEngineToPool(); - } + try + { + WriteScriptTag(writer, bodyWriter => bodyWriter.Write(Environment.GetInitJavaScript(clientOnly))); + } + finally + { + Environment.ReturnEngineToPool(); + } + }); } - private static IHtmlString GetScriptTag(string script) + private static void WriteScriptTag(TextWriter writer, Action bodyWriter) { -#if LEGACYASPNET - var tag = new TagBuilder("script") - { - InnerHtml = script, - }; - + writer.Write(""); - if (Environment.Configuration.ScriptNonceProvider != null) - { - tag.Attributes.Add("nonce", Environment.Configuration.ScriptNonceProvider()); - } + bodyWriter(writer); - return tag; -#endif - } - - // In ASP.NET Core, you can no longer call `.ToString` on `IHtmlString` - private static string RenderToString(IHtmlString source) - { -#if LEGACYASPNET - return source.ToString(); -#else - using (var writer = new StringWriter()) - { - source.WriteTo(writer, HtmlEncoder.Default); - return writer.ToString(); - } -#endif + writer.Write(""); } } } diff --git a/src/React.Web.Mvc4/React.Web.Mvc4.csproj b/src/React.Web.Mvc4/React.Web.Mvc4.csproj index a2e5c0043..a003753e9 100644 --- a/src/React.Web.Mvc4/React.Web.Mvc4.csproj +++ b/src/React.Web.Mvc4/React.Web.Mvc4.csproj @@ -21,7 +21,7 @@ - + true diff --git a/tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs b/tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs index b1937b58c..f2a41735f 100644 --- a/tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs +++ b/tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs @@ -50,7 +50,7 @@ public void ReactWithInitShouldReturnHtmlAndScript() componentName: "ComponentName", props: new { }, htmlTag: "span" - ); + ).ToHtmlString(); Assert.Equal( "HTML" + System.Environment.NewLine + "", result.ToString() @@ -91,7 +91,7 @@ public void ScriptNonceIsReturned() componentName: "ComponentName", props: new { }, htmlTag: "span" - ); + ).ToHtmlString(); Assert.Equal( "HTML" + System.Environment.NewLine + "", result.ToString() @@ -105,7 +105,7 @@ public void ScriptNonceIsReturned() componentName: "ComponentName", props: new { }, htmlTag: "span" - ); + ).ToHtmlString(); Assert.Equal( "HTML" + System.Environment.NewLine + "", result.ToString() @@ -134,7 +134,7 @@ public void EngineIsReturnedToPoolAfterRender() htmlTag: "span", clientOnly: true, serverOnly: false - ); + ).ToHtmlString(); component.Verify(x => x.RenderHtml(It.Is(y => y == true), It.Is(z => z == false), null), Times.Once); environment.Verify(x => x.ReturnEngineToPool(), Times.Once); } @@ -160,7 +160,7 @@ public void ReactWithClientOnlyTrueShouldCallRenderHtmlWithTrue() htmlTag: "span", clientOnly: true, serverOnly: false - ); + ).ToHtmlString(); component.Verify(x => x.RenderHtml(It.Is(y => y == true), It.Is(z => z == false), null), Times.Once); } @@ -185,7 +185,7 @@ public void ReactWithServerOnlyTrueShouldCallRenderHtmlWithTrue() htmlTag: "span", clientOnly: false, serverOnly: true - ); + ).ToHtmlString(); component.Verify(x => x.RenderHtml(It.Is(y => y == false), It.Is(z => z == true), null), Times.Once); } }