Skip to content

Commit

Permalink
Support component-level exception handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
dustinsoftware committed Dec 19, 2017
1 parent ea071c8 commit 4ef3074
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 42 deletions.
11 changes: 7 additions & 4 deletions src/React.AspNet/HtmlHelperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

using React.Exceptions;
using React.TinyIoC;
using System;

#if LEGACYASPNET
using System.Web;
Expand Down Expand Up @@ -64,7 +65,8 @@ public static IHtmlString React<T>(
string containerId = null,
bool clientOnly = false,
bool serverOnly = false,
string containerClass = null
string containerClass = null,
Action<Exception, string, string> exceptionHandler = null
)
{
try
Expand All @@ -78,7 +80,7 @@ public static IHtmlString React<T>(
{
reactComponent.ContainerClass = containerClass;
}
var result = reactComponent.RenderHtml(clientOnly, serverOnly);
var result = reactComponent.RenderHtml(clientOnly, serverOnly, exceptionHandler);
return new HtmlString(result);
}
finally
Expand Down Expand Up @@ -108,7 +110,8 @@ public static IHtmlString ReactWithInit<T>(
string htmlTag = null,
string containerId = null,
bool clientOnly = false,
string containerClass = null
string containerClass = null,
Action<Exception, string, string> exceptionHandler = null
)
{
try
Expand All @@ -122,7 +125,7 @@ public static IHtmlString ReactWithInit<T>(
{
reactComponent.ContainerClass = containerClass;
}
var html = reactComponent.RenderHtml(clientOnly);
var html = reactComponent.RenderHtml(clientOnly, exceptionHandler: exceptionHandler);

#if LEGACYASPNET
var script = new TagBuilder("script")
Expand Down
13 changes: 8 additions & 5 deletions src/React.Core/IReactComponent.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/*
/*
* 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;

namespace React
{
{
/// <summary>
/// Represents a React JavaScript component.
/// </summary>
Expand Down Expand Up @@ -44,9 +46,10 @@ public interface IReactComponent
/// return the rendered HTML.
/// </summary>
/// <param name="renderContainerOnly">Only renders component container. Used for client-side only rendering.</param>
/// <param name="renderServerOnly">Only renders the common HTML mark up and not any React specific data attributes. Used for server-side only rendering.</param>
/// <param name="renderServerOnly">Only renders the common HTML mark up and not any React specific data attributes. Used for server-side only rendering.</param>
/// <param name="exceptionHandler"></param>
/// <returns>HTML</returns>
string RenderHtml(bool renderContainerOnly = false, bool renderServerOnly = false);
string RenderHtml(bool renderContainerOnly = false, bool renderServerOnly = false, Action<Exception, string, string> exceptionHandler = null);

/// <summary>
/// Renders the JavaScript required to initialise this component client-side. This will
Expand Down
4 changes: 2 additions & 2 deletions src/React.Core/IReactSiteConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,14 @@ public interface IReactSiteConfiguration
/// An exception handler which will be called if a render exception is thrown.
/// If unset, unhandled exceptions will be thrown for all component renders.
/// </summary>
Action<Exception> ExceptionHandler { get; set; }
Action<Exception, string, string> ExceptionHandler { get; set; }

/// <summary>
/// Sets an exception handler which will be called if a render exception is thrown.
/// If unset, unhandled exceptions will be thrown for all component renders.
/// </summary>
/// <param name="handler"></param>
/// <returns></returns>
IReactSiteConfiguration SetExceptionHandler(Action<Exception> handler);
IReactSiteConfiguration SetExceptionHandler(Action<Exception, string, string> handler);
}
}
24 changes: 11 additions & 13 deletions src/React.Core/ReactComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* 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.Linq;
using System.Text.RegularExpressions;
Expand All @@ -15,7 +15,7 @@
using React.Exceptions;

namespace React
{
{
/// <summary>
/// Represents a React JavaScript component.
/// </summary>
Expand Down Expand Up @@ -106,9 +106,10 @@ public ReactComponent(IReactEnvironment environment, IReactSiteConfiguration con
/// return the rendered HTML.
/// </summary>
/// <param name="renderContainerOnly">Only renders component container. Used for client-side only rendering.</param>
/// <param name="renderServerOnly">Only renders the common HTML mark up and not any React specific data attributes. Used for server-side only rendering.</param>
/// <param name="renderServerOnly">Only renders the common HTML mark up and not any React specific data attributes. Used for server-side only rendering.</param>
/// <param name="exceptionHandler"></param>
/// <returns>HTML</returns>
public virtual string RenderHtml(bool renderContainerOnly = false, bool renderServerOnly = false)
public virtual string RenderHtml(bool renderContainerOnly = false, bool renderServerOnly = false, Action<Exception, string, string> exceptionHandler = null)
{
if (!_configuration.UseServerSideRendering)
{
Expand All @@ -132,15 +133,12 @@ public virtual string RenderHtml(bool renderContainerOnly = false, bool renderSe
}
catch (JsRuntimeException ex)
{
if (_configuration.ExceptionHandler == null) {
throw new ReactServerRenderingException(string.Format(
"Error while rendering \"{0}\" to \"{2}\": {1}",
ComponentName,
ex.Message,
ContainerId
));
if (exceptionHandler == null)
{
exceptionHandler = _configuration.ExceptionHandler;
}
_configuration.ExceptionHandler(ex);

exceptionHandler(ex, ComponentName, ContainerId);
}
}

Expand Down
12 changes: 10 additions & 2 deletions src/React.Core/ReactSiteConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using React.Exceptions;

namespace React
{
Expand Down Expand Up @@ -45,6 +46,13 @@ public ReactSiteConfiguration()
};
UseDebugReact = false;
UseServerSideRendering = true;
ExceptionHandler = (Exception ex, string ComponentName, string ContainerId) =>
throw new ReactServerRenderingException(string.Format(
"Error while rendering \"{0}\" to \"{2}\": {1}",
ComponentName,
ex.Message,
ContainerId
));
}

/// <summary>
Expand Down Expand Up @@ -306,14 +314,14 @@ public IReactSiteConfiguration DisableServerSideRendering()
/// Handle an exception caught during server-render of a component.
/// If unset, unhandled exceptions will be thrown for all component renders.
/// </summary>
public Action<Exception> ExceptionHandler { get; set; }
public Action<Exception, string, string> ExceptionHandler { get; set; }

/// <summary>
///
/// </summary>
/// <param name="handler"></param>
/// <returns></returns>
public IReactSiteConfiguration SetExceptionHandler(Action<Exception> handler)
public IReactSiteConfiguration SetExceptionHandler(Action<Exception, string, string> handler)
{
ExceptionHandler = handler;
return this;
Expand Down
6 changes: 3 additions & 3 deletions src/React.Sample.CoreMvc/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* 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 Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand Down Expand Up @@ -71,7 +71,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF
config
.SetReuseJavaScriptEngines(true)
.AddScript("~/js/Sample.jsx")
.SetExceptionHandler(ex =>
.SetExceptionHandler((ex, name, id) =>
{
Logger.LogError("React component exception thrown!" + ex.ToString());
})
Expand Down
13 changes: 10 additions & 3 deletions tests/React.Tests/Core/ReactComponentTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* 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 JavaScriptEngineSwitcher.Core;
using Moq;
Expand Down Expand Up @@ -204,6 +204,7 @@ public void ExceptionThrownIsHandled()

var config = new Mock<IReactSiteConfiguration>();
config.Setup(x => x.UseServerSideRendering).Returns(true);
config.Setup(x => x.ExceptionHandler).Returns(() => throw new ReactServerRenderingException("test"));

var component = new ReactComponent(environment.Object, config.Object, "Foo", "container")
{
Expand All @@ -223,9 +224,15 @@ public void ExceptionThrownIsHandled()

Assert.True(exceptionCaught);

// Custom handler passed into render call
bool customHandlerInvoked = false;
Action<Exception, string, string> customHandler = (ex, name, id) => customHandlerInvoked = true;
component.RenderHtml(exceptionHandler: customHandler);
Assert.True(customHandlerInvoked);

// Custom exception handler set
Exception caughtException = null;
config.Setup(x => x.ExceptionHandler).Returns(ex => caughtException = ex);
config.Setup(x => x.ExceptionHandler).Returns((ex, name, id) => caughtException = ex);

var result = component.RenderHtml();
Assert.Equal(@"<div id=""container""></div>", result);
Expand Down
20 changes: 10 additions & 10 deletions tests/React.Tests/Mvc/HtmlHelperExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* 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 Moq;
using Xunit;
using React.Web.Mvc;
using Xunit;

namespace React.Tests.Mvc
{
Expand All @@ -31,7 +31,7 @@ private Mock<IReactEnvironment> ConfigureMockEnvironment()
public void ReactWithInitShouldReturnHtmlAndScript()
{
var component = new Mock<IReactComponent>();
component.Setup(x => x.RenderHtml(false, false)).Returns("HTML");
component.Setup(x => x.RenderHtml(false, false, null)).Returns("HTML");
component.Setup(x => x.RenderJavaScript()).Returns("JS");
var environment = ConfigureMockEnvironment();
environment.Setup(x => x.CreateComponent(
Expand All @@ -57,7 +57,7 @@ public void ReactWithInitShouldReturnHtmlAndScript()
public void EngineIsReturnedToPoolAfterRender()
{
var component = new Mock<IReactComponent>();
component.Setup(x => x.RenderHtml(true, true)).Returns("HTML");
component.Setup(x => x.RenderHtml(true, true, null)).Returns("HTML");
var environment = ConfigureMockEnvironment();
environment.Setup(x => x.CreateComponent(
"ComponentName",
Expand All @@ -75,15 +75,15 @@ public void EngineIsReturnedToPoolAfterRender()
clientOnly: true,
serverOnly: true
);
component.Verify(x => x.RenderHtml(It.Is<bool>(y => y == true), It.Is<bool>(z => z == true)), Times.Once);
component.Verify(x => x.RenderHtml(It.Is<bool>(y => y == true), It.Is<bool>(z => z == true), null), Times.Once);
environment.Verify(x => x.ReturnEngineToPool(), Times.Once);
}

[Fact]
public void ReactWithClientOnlyTrueShouldCallRenderHtmlWithTrue()
{
var component = new Mock<IReactComponent>();
component.Setup(x => x.RenderHtml(true, true)).Returns("HTML");
component.Setup(x => x.RenderHtml(true, true, null)).Returns("HTML");
var environment = ConfigureMockEnvironment();
environment.Setup(x => x.CreateComponent(
"ComponentName",
Expand All @@ -100,13 +100,13 @@ public void ReactWithClientOnlyTrueShouldCallRenderHtmlWithTrue()
clientOnly: true,
serverOnly: true
);
component.Verify(x => x.RenderHtml(It.Is<bool>(y => y == true), It.Is<bool>(z => z == true)), Times.Once);
component.Verify(x => x.RenderHtml(It.Is<bool>(y => y == true), It.Is<bool>(z => z == true), null), Times.Once);
}

[Fact]
public void ReactWithServerOnlyTrueShouldCallRenderHtmlWithTrue() {
var component = new Mock<IReactComponent>();
component.Setup(x => x.RenderHtml(true, true)).Returns("HTML");
component.Setup(x => x.RenderHtml(true, true, null)).Returns("HTML");
var environment = ConfigureMockEnvironment();
environment.Setup(x => x.CreateComponent(
"ComponentName",
Expand All @@ -123,7 +123,7 @@ public void ReactWithServerOnlyTrueShouldCallRenderHtmlWithTrue() {
clientOnly: true,
serverOnly: true
);
component.Verify(x => x.RenderHtml(It.Is<bool>(y => y == true), It.Is<bool>(z => z == true)), Times.Once);
component.Verify(x => x.RenderHtml(It.Is<bool>(y => y == true), It.Is<bool>(z => z == true), null), Times.Once);
}
}
}

0 comments on commit 4ef3074

Please sign in to comment.