Browse Source

Update HttpLoggingAttribute API (#48214)

Brennan 2 years ago
parent
commit
7db7dcdd79

+ 53 - 12
src/Middleware/HttpLogging/src/HttpLoggingAttribute.cs

@@ -13,32 +13,73 @@ public sealed class HttpLoggingAttribute : Attribute
     /// Initializes an instance of the <see cref="HttpLoggingAttribute"/> class.
     /// Initializes an instance of the <see cref="HttpLoggingAttribute"/> class.
     /// </summary>
     /// </summary>
     /// <param name="loggingFields">Specifies what fields to log for the endpoint.</param>
     /// <param name="loggingFields">Specifies what fields to log for the endpoint.</param>
-    /// <param name="requestBodyLogLimit">Specifies the maximum number of bytes to be logged for the request body. A value of <c>-1</c> means use the default setting in <see cref="HttpLoggingOptions.RequestBodyLogLimit"/>.</param>
-    /// <param name="responseBodyLogLimit">Specifies the maximum number of bytes to be logged for the response body. A value of <c>-1</c> means use the default setting in <see cref="HttpLoggingOptions.ResponseBodyLogLimit"/>.</param>
-    /// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="requestBodyLogLimit"/> or <paramref name="responseBodyLogLimit"/> is less than <c>-1</c>.</exception>
-    public HttpLoggingAttribute(HttpLoggingFields loggingFields, int requestBodyLogLimit = -1, int responseBodyLogLimit = -1)
+    public HttpLoggingAttribute(HttpLoggingFields loggingFields)
     {
     {
         LoggingFields = loggingFields;
         LoggingFields = loggingFields;
-
-        ArgumentOutOfRangeException.ThrowIfLessThan(requestBodyLogLimit, -1);
-        ArgumentOutOfRangeException.ThrowIfLessThan(responseBodyLogLimit, -1);
-
-        RequestBodyLogLimit = requestBodyLogLimit;
-        ResponseBodyLogLimit = responseBodyLogLimit;
     }
     }
 
 
+    private int _responseBodyLogLimit;
+    private int _requestBodyLogLimit;
+
     /// <summary>
     /// <summary>
     /// Specifies what fields to log.
     /// Specifies what fields to log.
     /// </summary>
     /// </summary>
     public HttpLoggingFields LoggingFields { get; }
     public HttpLoggingFields LoggingFields { get; }
 
 
+    /// <summary>
+    /// Indicates whether <see cref="RequestBodyLogLimit"/> has been set.
+    /// </summary>
+    public bool IsRequestBodyLogLimitSet { get; private set; }
+
     /// <summary>
     /// <summary>
     /// Specifies the maximum number of bytes to be logged for the request body.
     /// Specifies the maximum number of bytes to be logged for the request body.
     /// </summary>
     /// </summary>
-    public int RequestBodyLogLimit { get; }
+    /// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="RequestBodyLogLimit"/> set to a value less than <c>0</c>.</exception>
+    /// <exception cref="InvalidOperationException">Thrown when getting <see cref="RequestBodyLogLimit"/> if it hasn't been set to a value. Check <see cref="IsRequestBodyLogLimitSet"/> first.</exception>
+    public int RequestBodyLogLimit
+    {
+        get
+        {
+            if (IsRequestBodyLogLimitSet)
+            {
+                return _requestBodyLogLimit;
+            }
+
+            throw new InvalidOperationException($"{nameof(RequestBodyLogLimit)} was not set. Check {nameof(IsRequestBodyLogLimitSet)} before accessing this property.");
+        }
+        set
+        {
+            ArgumentOutOfRangeException.ThrowIfLessThan(value, 0, nameof(RequestBodyLogLimit));
+            _requestBodyLogLimit = value;
+            IsRequestBodyLogLimitSet = true;
+        }
+    }
+
+    /// <summary>
+    /// Indicates whether <see cref="ResponseBodyLogLimit"/> has been set.
+    /// </summary>
+    public bool IsResponseBodyLogLimitSet { get; private set; }
 
 
     /// <summary>
     /// <summary>
     /// Specifies the maximum number of bytes to be logged for the response body.
     /// Specifies the maximum number of bytes to be logged for the response body.
     /// </summary>
     /// </summary>
-    public int ResponseBodyLogLimit { get; }
+    /// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="ResponseBodyLogLimit"/> set to a value less than <c>0</c>.</exception>
+    /// <exception cref="InvalidOperationException">Thrown when getting <see cref="ResponseBodyLogLimit"/> if it hasn't been set to a value. Check <see cref="IsResponseBodyLogLimitSet"/> first.</exception>
+    public int ResponseBodyLogLimit
+    {
+        get
+        {
+            if (IsResponseBodyLogLimitSet)
+            {
+                return _responseBodyLogLimit;
+            }
+            throw new InvalidOperationException($"{nameof(ResponseBodyLogLimit)} was not set. Check {nameof(IsResponseBodyLogLimitSet)} before accessing this property.");
+        }
+        set
+        {
+            ArgumentOutOfRangeException.ThrowIfLessThan(value, 0, nameof(ResponseBodyLogLimit));
+            _responseBodyLogLimit = value;
+            IsResponseBodyLogLimitSet = true;
+        }
+    }
 }
 }

+ 14 - 3
src/Middleware/HttpLogging/src/HttpLoggingEndpointConventionBuilderExtensions.cs

@@ -19,11 +19,22 @@ public static class HttpLoggingEndpointConventionBuilderExtensions
     /// <param name="requestBodyLogLimit">Sets the <see cref="HttpLoggingOptions.RequestBodyLogLimit"/> for this endpoint. A value of <c>-1</c> means use the default setting in <see cref="HttpLoggingOptions.RequestBodyLogLimit"/>.</param>
     /// <param name="requestBodyLogLimit">Sets the <see cref="HttpLoggingOptions.RequestBodyLogLimit"/> for this endpoint. A value of <c>-1</c> means use the default setting in <see cref="HttpLoggingOptions.RequestBodyLogLimit"/>.</param>
     /// <param name="responseBodyLogLimit">Sets the <see cref="HttpLoggingOptions.ResponseBodyLogLimit"/> for this endpoint. A value of <c>-1</c> means use the default setting in <see cref="HttpLoggingOptions.ResponseBodyLogLimit"/>.</param>
     /// <param name="responseBodyLogLimit">Sets the <see cref="HttpLoggingOptions.ResponseBodyLogLimit"/> for this endpoint. A value of <c>-1</c> means use the default setting in <see cref="HttpLoggingOptions.ResponseBodyLogLimit"/>.</param>
     /// <returns>The original convention builder parameter.</returns>
     /// <returns>The original convention builder parameter.</returns>
-    /// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="requestBodyLogLimit"/> or <paramref name="responseBodyLogLimit"/> is less than <c>-1</c>.</exception>
-    public static TBuilder WithHttpLogging<TBuilder>(this TBuilder builder, HttpLoggingFields loggingFields, int requestBodyLogLimit = -1, int responseBodyLogLimit = -1) where TBuilder : IEndpointConventionBuilder
+    /// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="requestBodyLogLimit"/> or <paramref name="responseBodyLogLimit"/> is less than <c>0</c>.</exception>
+    public static TBuilder WithHttpLogging<TBuilder>(this TBuilder builder, HttpLoggingFields loggingFields, int? requestBodyLogLimit = null, int? responseBodyLogLimit = null) where TBuilder : IEndpointConventionBuilder
     {
     {
         // Construct outside build.Add lambda to allow exceptions to be thrown immediately
         // Construct outside build.Add lambda to allow exceptions to be thrown immediately
-        var metadata = new HttpLoggingAttribute(loggingFields, requestBodyLogLimit, responseBodyLogLimit);
+        var metadata = new HttpLoggingAttribute(loggingFields);
+
+        if (requestBodyLogLimit is not null)
+        {
+            ArgumentOutOfRangeException.ThrowIfLessThan(requestBodyLogLimit.Value, 0, nameof(requestBodyLogLimit));
+            metadata.RequestBodyLogLimit = requestBodyLogLimit.Value;
+        }
+        if (responseBodyLogLimit is not null)
+        {
+            ArgumentOutOfRangeException.ThrowIfLessThan(responseBodyLogLimit.Value, 0, nameof(responseBodyLogLimit));
+            metadata.ResponseBodyLogLimit = responseBodyLogLimit.Value;
+        }
 
 
         builder.Add(endpointBuilder =>
         builder.Add(endpointBuilder =>
         {
         {

+ 2 - 2
src/Middleware/HttpLogging/src/HttpLoggingMiddleware.cs

@@ -110,7 +110,7 @@ internal sealed class HttpLoggingMiddleware
                     out var encoding))
                     out var encoding))
                 {
                 {
                     var requestBodyLogLimit = options.RequestBodyLogLimit;
                     var requestBodyLogLimit = options.RequestBodyLogLimit;
-                    if (loggingAttribute?.RequestBodyLogLimit is int)
+                    if (loggingAttribute?.IsRequestBodyLogLimitSet is true)
                     {
                     {
                         requestBodyLogLimit = loggingAttribute.RequestBodyLogLimit;
                         requestBodyLogLimit = loggingAttribute.RequestBodyLogLimit;
                     }
                     }
@@ -161,7 +161,7 @@ internal sealed class HttpLoggingMiddleware
                 originalBodyFeature = context.Features.Get<IHttpResponseBodyFeature>()!;
                 originalBodyFeature = context.Features.Get<IHttpResponseBodyFeature>()!;
 
 
                 var responseBodyLogLimit = options.ResponseBodyLogLimit;
                 var responseBodyLogLimit = options.ResponseBodyLogLimit;
-                if (loggingAttribute?.ResponseBodyLogLimit is int)
+                if (loggingAttribute?.IsRequestBodyLogLimitSet is true)
                 {
                 {
                     responseBodyLogLimit = loggingAttribute.ResponseBodyLogLimit;
                     responseBodyLogLimit = loggingAttribute.ResponseBodyLogLimit;
                 }
                 }

+ 6 - 2
src/Middleware/HttpLogging/src/PublicAPI.Unshipped.txt

@@ -1,8 +1,12 @@
 #nullable enable
 #nullable enable
 Microsoft.AspNetCore.Builder.HttpLoggingEndpointConventionBuilderExtensions
 Microsoft.AspNetCore.Builder.HttpLoggingEndpointConventionBuilderExtensions
 Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute
 Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute
-Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.HttpLoggingAttribute(Microsoft.AspNetCore.HttpLogging.HttpLoggingFields loggingFields, int requestBodyLogLimit = -1, int responseBodyLogLimit = -1) -> void
+Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.HttpLoggingAttribute(Microsoft.AspNetCore.HttpLogging.HttpLoggingFields loggingFields) -> void
+Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.IsRequestBodyLogLimitSet.get -> bool
+Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.IsResponseBodyLogLimitSet.get -> bool
 Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.LoggingFields.get -> Microsoft.AspNetCore.HttpLogging.HttpLoggingFields
 Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.LoggingFields.get -> Microsoft.AspNetCore.HttpLogging.HttpLoggingFields
 Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.RequestBodyLogLimit.get -> int
 Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.RequestBodyLogLimit.get -> int
+Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.RequestBodyLogLimit.set -> void
 Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.ResponseBodyLogLimit.get -> int
 Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.ResponseBodyLogLimit.get -> int
-static Microsoft.AspNetCore.Builder.HttpLoggingEndpointConventionBuilderExtensions.WithHttpLogging<TBuilder>(this TBuilder builder, Microsoft.AspNetCore.HttpLogging.HttpLoggingFields loggingFields, int requestBodyLogLimit = -1, int responseBodyLogLimit = -1) -> TBuilder
+Microsoft.AspNetCore.HttpLogging.HttpLoggingAttribute.ResponseBodyLogLimit.set -> void
+static Microsoft.AspNetCore.Builder.HttpLoggingEndpointConventionBuilderExtensions.WithHttpLogging<TBuilder>(this TBuilder builder, Microsoft.AspNetCore.HttpLogging.HttpLoggingFields loggingFields, int? requestBodyLogLimit = null, int? responseBodyLogLimit = null) -> TBuilder

+ 26 - 0
src/Middleware/HttpLogging/test/HttpLoggingAttributeTests.cs

@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.HttpLogging.Tests;
+
+public class HttpLoggingAttributeTests
+{
+    [Fact]
+    public void ThrowsForInvalidOptions()
+    {
+        var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new HttpLoggingAttribute(HttpLoggingFields.None) { RequestBodyLogLimit = -1 });
+        Assert.Equal(nameof(HttpLoggingAttribute.RequestBodyLogLimit), ex.ParamName);
+
+        ex = Assert.Throws<ArgumentOutOfRangeException>(() => new HttpLoggingAttribute(HttpLoggingFields.None) { ResponseBodyLogLimit = -1 });
+        Assert.Equal(nameof(HttpLoggingAttribute.ResponseBodyLogLimit), ex.ParamName);
+    }
+
+    [Fact]
+    public void ThrowsWhenAccessingFieldsThatAreNotSet()
+    {
+        var attribute = new HttpLoggingAttribute(HttpLoggingFields.None);
+
+        Assert.Throws<InvalidOperationException>(() => attribute.RequestBodyLogLimit);
+        Assert.Throws<InvalidOperationException>(() => attribute.ResponseBodyLogLimit);
+    }
+}

+ 2 - 2
src/Middleware/HttpLogging/test/HttpLoggingEndpointConventionBuilderTests.cs

@@ -42,11 +42,11 @@ public class HttpLoggingEndpointConventionBuilderTests
 
 
         // Act & Assert
         // Act & Assert
         var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
         var ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
-            testConventionBuilder.WithHttpLogging(HttpLoggingFields.None, requestBodyLogLimit: -2));
+            testConventionBuilder.WithHttpLogging(HttpLoggingFields.None, requestBodyLogLimit: -1));
         Assert.Equal("requestBodyLogLimit", ex.ParamName);
         Assert.Equal("requestBodyLogLimit", ex.ParamName);
 
 
         ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
         ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
-            testConventionBuilder.WithHttpLogging(HttpLoggingFields.None, responseBodyLogLimit: -2));
+            testConventionBuilder.WithHttpLogging(HttpLoggingFields.None, responseBodyLogLimit: -1));
         Assert.Equal("responseBodyLogLimit", ex.ParamName);
         Assert.Equal("responseBodyLogLimit", ex.ParamName);
     }
     }
 }
 }

+ 1 - 1
src/Middleware/HttpLogging/test/HttpLoggingMiddlewareTests.cs

@@ -1337,7 +1337,7 @@ public class HttpLoggingMiddlewareTests : LoggedTest
                                 return "testing";
                                 return "testing";
                             }).WithHttpLogging((HttpLoggingFields.Request & ~HttpLoggingFields.RequestScheme) | (HttpLoggingFields.Response & ~HttpLoggingFields.ResponseStatusCode));
                             }).WithHttpLogging((HttpLoggingFields.Request & ~HttpLoggingFields.RequestScheme) | (HttpLoggingFields.Response & ~HttpLoggingFields.ResponseStatusCode));
 
 
-                            endpoint.MapGet("/attr_restrictedsize", [HttpLogging(HttpLoggingFields.Request | HttpLoggingFields.Response, requestBodyLogLimit: 3, responseBodyLogLimit: 6)] async (HttpContext c) =>
+                            endpoint.MapGet("/attr_restrictedsize", [HttpLogging(HttpLoggingFields.Request | HttpLoggingFields.Response, RequestBodyLogLimit = 3, ResponseBodyLogLimit = 6)] async (HttpContext c) =>
                             {
                             {
                                 await c.Request.Body.ReadAsync(new byte[100]);
                                 await c.Request.Body.ReadAsync(new byte[100]);
                                 return "testing";
                                 return "testing";