Skip to content

Commit

Permalink
Added ${aspnet-request-form} (#313)
Browse files Browse the repository at this point in the history
* Added aspnet-request-form layout renderer that outputs all or some of the request form collection.

* Made the aspnet-request-form layout renderer output uniform in both asp.net core and .net

* Added unit tests for aspnet-request-form layout renderer.

* Fixed issues with the unit tests for aspnet-request-form not working in the AspNetCore.Tests project.

* Removed code duplication by changing to a foreach that works for both AspNetCore and .net framework .

* Changed Include and Exclude to case insensitive HashSets.

* Added unit test to cove exclude/include precedence and updated comments.

* Made AspNetRequestFormLayoutRenderer a subclass of AspNetLayoutMultiValueRendererBase
  • Loading branch information
DrewBrasher authored and 304NotModified committed Sep 22, 2018
1 parent 9d95873 commit 21989ad
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using NLog.Web.Tests.LayoutRenderers;
using System;
using System.Collections.Generic;
using System.Text;
using NLog.Web.LayoutRenderers;
using NLog.Web.Tests;
using NSubstitute;
using System.Collections.Specialized;
#if ASP_NET_CORE
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
#else
using System.Web;
#endif
using Xunit;

namespace NLog.Web.Tests.LayoutRenderers
{
public class AspNetRequestFormLayoutRendererTests : TestInvolvingAspNetHttpContext
{
[Fact]
public void ShouldReturnEmptyIfFormCollectionIsEmpty()
{
// Arrange
var expectedResult = "";
var renderer = CreateRenderer(false);

// Act
string result = renderer.Render(new LogEventInfo());

// Assert
Assert.Equal(expectedResult, result);
}

[Fact]
public void ShouldReturnAllIfDefaultsAreUsed()
{
// Arrange
var expectedResult = "id=1,name=Test Person,token=86abe8fe-2237-4f87-81af-0a4e522b4140";
var renderer = CreateRenderer();

// Act
string result = renderer.Render(new LogEventInfo());

// Assert
Assert.Equal(expectedResult, result);
}

[Fact]
public void ShouldReturnOnlySpecifiedIfIncludeIsUsed()
{
// Arrange
var expectedResult = "id=1,name=Test Person";
var renderer = CreateRenderer();
renderer.Include.Add("id");
renderer.Include.Add("name");

// Act
string result = renderer.Render(new LogEventInfo());

// Assert
Assert.Equal(expectedResult, result);
}

[Fact]
public void ShouldNotReturnKeysSpecidiedInExclude()
{
// Arrange
var expectedResult = "id=1,name=Test Person";
var renderer = CreateRenderer();
renderer.Exclude.Add("token");

// Act
string result = renderer.Render(new LogEventInfo());

// Assert
Assert.Equal(expectedResult, result);
}

[Fact]
public void ShouldUseTheSpecifiedSeparator()
{
// Arrange
var expectedResult = "id=1\r\nname=Test Person\r\ntoken=86abe8fe-2237-4f87-81af-0a4e522b4140";
var renderer = CreateRenderer();
renderer.ItemSeparator = "${newline}";

// Act
string result = renderer.Render(new LogEventInfo());

// Assert
Assert.Equal(expectedResult, result);
}

[Fact]
public void ExcludeShouldTakePrecedenceOverInclude()
{
// Arrange
var expectedResult = "name=Test Person";
var renderer = CreateRenderer();
renderer.Include.Add("id");
renderer.Include.Add("name");
renderer.Exclude.Add("id");

// Act
string result = renderer.Render(new LogEventInfo());

// Assert
Assert.Equal(expectedResult, result);
}

private AspNetRequestFormLayoutRenderer CreateRenderer(bool hasFormValues = true)
{
#if ASP_NET_CORE
var httpContext = this.HttpContext;
httpContext.Request.ContentType = "application/x-www-form-urlencoded";
#else
var httpContext = Substitute.For<HttpContextBase>();
httpContext.Request.ContentType.Returns("application/x-www-form-urlencoded");
#endif

if (hasFormValues)
{
#if ASP_NET_CORE
var formCollection = new FormCollection(new Dictionary<string, StringValues>{
{ "id","1" },
{ "name","Test Person" },
{ "token","86abe8fe-2237-4f87-81af-0a4e522b4140" }
});
httpContext.Request.Form = formCollection;
#else
var formCollection = new NameValueCollection(){
{ "id","1" },
{ "name","Test Person" },
{ "token","86abe8fe-2237-4f87-81af-0a4e522b4140" }
};
httpContext.Request.Form.Returns(formCollection);
#endif
}

return new AspNetRequestFormLayoutRenderer
{
HttpContextAccessor = new FakeHttpContextAccessor(httpContext)
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using NLog.LayoutRenderers;
using NLog.Web.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NLog.Web.LayoutRenderers
{
/// <summary>
/// ASP.NET Request Form Data
/// </summary>
/// <example>
/// <para>Example usage of ${aspnet-request-form}:</para>
/// <code lang="NLog Layout Renderer">
/// ${aspnet-request-form} - Produces - All Form Data from the Request with each key/value pair separated by a comma.
/// ${aspnet-request-form:Include=id,name} - Produces - Only Form Data from the Request with keys "id" and "name".
/// ${aspnet-request-form:Exclude=id,name} - Produces - All Form Data from the Request except the keys "id" and "name".
/// ${aspnet-request-form:Include=id,name:Exclude=id} - Produces - Only Form Data from the Request with key "name" (<see cref="Exclude"/> takes precedence over <see cref="Include"/>).
/// ${aspnet-request-form:ItemSeparator=${newline}} - Produces - All Form Data from the Request with each key/value pair separated by a new line.
/// </code>
/// </example>
[LayoutRenderer("aspnet-request-form")]
public class AspNetRequestFormLayoutRenderer : AspNetLayoutMultiValueRendererBase
{
/// <summary>
/// Gets or sets the form keys to include in the output. If omitted, all are included. <see cref="Exclude"/> takes precedence over <see cref="Include"/>.
/// </summary>
/// <docgen category='Rendering Options' order='10' />
#if ASP_NET_CORE
public ISet<string> Include { get; set; }
#else
public HashSet<string> Include { get; set; }
#endif

/// <summary>
/// Gets or sets the form keys to exclude from the output. If omitted, none are excluded. <see cref="Exclude"/> takes precedence over <see cref="Include"/>.
/// </summary>
/// <docgen category='Rendering Options' order='10' />
#if ASP_NET_CORE
public ISet<string> Exclude { get; set; }
#else
public HashSet<string> Exclude { get; set; }
#endif

/// <summary>
/// Constructor
/// </summary>
public AspNetRequestFormLayoutRenderer()
{
Include = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
Exclude = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
}

/// <summary>
/// Renders the Form Collection from the HttpRequest and appends it to the specified <see cref="StringBuilder" />.
/// </summary>
/// <param name="builder"></param>
/// <param name="logEvent"></param>
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
if (HttpContextAccessor?.HttpContext?.TryGetRequest() == null)
{
return;
}

var formDataToInclude = GetPairsToInclude();

if (formDataToInclude.Any())
{
SerializePairs(formDataToInclude, builder, logEvent);
}
}

private IEnumerable<KeyValuePair<string, string>> GetPairsToInclude()
{
var httpRequest = HttpContextAccessor?.HttpContext?.TryGetRequest();
var pairs = new List<KeyValuePair<string, string>>();

if (httpRequest.Form != null)
{
foreach (string key in httpRequest.Form.Keys)
{
if ((!Include.Any() || Include.Contains(key)) && !Exclude.Contains(key))
{
pairs.Add(new KeyValuePair<string, string>(key, httpRequest.Form[key]));
}
}
}

return pairs;
}
}
}
3 changes: 3 additions & 0 deletions NLog.Web.Tests/NLog.Web.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
<Compile Include="..\NLog.Web.AspNetCore.Tests\LayoutRenderers\AspNetMvcActionRendererTests.cs" />
<Compile Include="..\NLog.Web.AspNetCore.Tests\LayoutRenderers\AspNetCookieLayoutRendererTests.cs" />
<Compile Include="..\NLog.Web.AspNetCore.Tests\LayoutRenderers\AspNetItemValueLayoutRendererTests.cs" />
<Compile Include="..\NLog.Web.AspNetCore.Tests\LayoutRenderers\AspNetRequestFormLayoutRendererTests.cs">
<Link>AspNetRequestFormLayoutRendererTests.cs</Link>
</Compile>
<Compile Include="..\NLog.Web.AspNetCore.Tests\LayoutRenderers\AspNetRequestValueLayoutRendererTests.cs" />
<Compile Include="..\NLog.Web.AspNetCore.Tests\LayoutRenderers\AspNetSessionIDLayoutRendererTests.cs" />
<Compile Include="..\NLog.Web.AspNetCore.Tests\LayoutRenderers\AspNetSessionValueLayoutRendererTests.cs" />
Expand Down
3 changes: 3 additions & 0 deletions NLog.Web/NLog.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
<Compile Include="..\NLog.Web.AspNetCore\LayoutRenderers\AspNetMvcControllerRenderer.cs" />
<Compile Include="..\NLog.Web.AspNetCore\LayoutRenderers\AspNetMvcActionRenderer.cs" />
<Compile Include="..\NLog.Web.AspNetCore\LayoutRenderers\AspNetMvcLayoutRendererBase.cs" />
<Compile Include="..\NLog.Web.AspNetCore\LayoutRenderers\AspNetRequestFormLayoutRenderer.cs">
<Link>AspNetRequestFormLayoutRenderer.cs</Link>
</Compile>
<Compile Include="..\NLog.Web.AspNetCore\LayoutRenderers\AspNetRequestValueLayoutRenderer.cs" />
<Compile Include="..\NLog.Web.AspNetCore\LayoutRenderers\AspNetRequestIpLayoutRenderer.cs" />
<Compile Include="..\NLog.Web.AspNetCore\LayoutRenderers\AspNetSessionIdLayoutRenderer.cs" />
Expand Down

0 comments on commit 21989ad

Please sign in to comment.