Skip to content

Improve endpoint metadata debugging #48207

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bbd0058
Override the ToString method in the DataTokensMetadata class.
SeanFarrow Mar 12, 2023
6252be4
Override the ToString method in the EndpointNameMetadata class.
SeanFarrow Mar 12, 2023
9bcccae
Override the ToString method in the HostAttribute class.
SeanFarrow Mar 12, 2023
ae62eec
Use the overridden ToString method of the HostAttribute class rather …
SeanFarrow Mar 12, 2023
c622400
Override the ToString method in the HttpMethodMetadata class.
SeanFarrow Mar 12, 2023
2810c06
Use the ToString method of the HttpMethodMetadata for debugging and r…
SeanFarrow Mar 12, 2023
b4e3786
Override the ToString method in the RouteNameMetadata class.
SeanFarrow Mar 12, 2023
19e069c
Use the newly implemented ToString method instead of DebuggerToString…
SeanFarrow Mar 12, 2023
a388d8e
Override the ToString method in the SuppressLinkGenerationMetadata cl…
SeanFarrow Mar 12, 2023
3cf3bb1
Override the ToString method in the SuppressMatchingMetadata class.
SeanFarrow Mar 12, 2023
1dcbd3f
Update the ToString method of the DataTokensMetadata class to remove …
SeanFarrow Mar 14, 2023
9000058
Override the ToString method in the DataTokensMetadata class.
SeanFarrow Mar 12, 2023
fc093b2
Override the ToString method in the HostAttribute class.
SeanFarrow Mar 12, 2023
1c2cc7f
Override the ToString method in the HttpMethodMetadata class.
SeanFarrow Mar 12, 2023
17ec7f6
Override the ToString method in the RouteNameMetadata class.
SeanFarrow Mar 12, 2023
4f2b05a
Update the ToString method of the DataTokensMetadata class to remove …
SeanFarrow Mar 14, 2023
72d493e
Clean up, collection proxy, extra attributes
JamesNK May 12, 2023
ee43ced
Update
JamesNK May 12, 2023
9fece53
Tests
JamesNK May 12, 2023
abe4ece
Add more attributes
JamesNK May 12, 2023
6fdf4a1
Fix build
JamesNK May 12, 2023
2c8d162
Fix tests
JamesNK May 12, 2023
a3acf62
Fix test
JamesNK May 12, 2023
1e69c9a
Update
JamesNK May 12, 2023
4af1ea4
PR feedback
JamesNK May 13, 2023
cf99e7f
Add debugger helper
JamesNK May 17, 2023
ca2b203
Update
JamesNK May 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Http/Http.Abstractions/src/Routing/Endpoint.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace Microsoft.AspNetCore.Http;

/// <summary>
/// Represents a logical endpoint in an application.
/// </summary>
[DebuggerDisplay("{ToString(),nq}")]
public class Endpoint
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;

Expand All @@ -16,6 +17,8 @@ namespace Microsoft.AspNetCore.Http;
/// of arbitrary types. The metadata items are stored as an ordered collection with
/// items arranged in ascending order of precedence.
/// </remarks>
[DebuggerTypeProxy(typeof(EndpointMetadataCollectionDebugView))]
[DebuggerDisplay("Count = {Count}")]
public sealed class EndpointMetadataCollection : IReadOnlyList<object>
{
/// <summary>
Expand Down Expand Up @@ -215,4 +218,27 @@ public void Reset()
_current = null;
}
}

private sealed class EndpointMetadataCollectionDebugView
{
private readonly EndpointMetadataCollection _collection;

public EndpointMetadataCollectionDebugView(EndpointMetadataCollection collection)
{
ArgumentNullException.ThrowIfNull(collection);

_collection = collection;
}

[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public object[] Items
{
get
{
var items = new object[_collection.Count];
_collection._items.CopyTo(items, 0);
return items;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Microsoft.AspNetCore.Http.Metadata;

namespace Microsoft.AspNetCore.Http;
Expand All @@ -13,6 +14,7 @@ namespace Microsoft.AspNetCore.Http;
/// can be used to annotate endpoints with detailed, multiline descriptors of their behavior.
/// </remarks>
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
[DebuggerDisplay("{ToString(),nq}")]
public sealed class EndpointDescriptionAttribute : Attribute, IEndpointDescriptionMetadata
{
/// <summary>
Expand All @@ -26,4 +28,10 @@ public EndpointDescriptionAttribute(string description)

/// <inheritdoc />
public string Description { get; }

/// <inheritdoc/>>
public override string ToString()
{
return $"Description: {Description ?? "(null)"}";
}
}
9 changes: 9 additions & 0 deletions src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Shared;

namespace Microsoft.AspNetCore.Http;

/// <summary>
/// Specifies a summary in <see cref="Endpoint.Metadata"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
[DebuggerDisplay("{ToString(),nq}")]
public sealed class EndpointSummaryAttribute : Attribute, IEndpointSummaryMetadata
{
/// <summary>
Expand All @@ -22,4 +25,10 @@ public EndpointSummaryAttribute(string summary)

/// <inheritdoc />
public string Summary { get; }

/// <inheritdoc/>>
public override string ToString()
{
return DebuggerHelpers.GetDebugText(nameof(Summary), Summary);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<Compile Include="$(SharedSourceRoot)ValueStringBuilder\**\*.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)Json\JsonSerializerExtensions.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)RouteHandlers\ExecuteHandlerHelper.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)Debugger\DebuggerHelpers.cs" LinkBase="Shared" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#nullable enable
override Microsoft.AspNetCore.Http.EndpointDescriptionAttribute.ToString() -> string!
override Microsoft.AspNetCore.Http.EndpointSummaryAttribute.ToString() -> string!
override Microsoft.AspNetCore.Http.TagsAttribute.ToString() -> string!
static Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(this Microsoft.AspNetCore.Http.HttpRequest! request, System.Text.Json.Serialization.Metadata.JsonTypeInfo! jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<object?>
static Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.WriteAsJsonAsync(this Microsoft.AspNetCore.Http.HttpResponse! response, object? value, System.Text.Json.Serialization.Metadata.JsonTypeInfo! jsonTypeInfo, string? contentType = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
Microsoft.AspNetCore.Mvc.ProblemDetails.Extensions.set -> void (forwarded, contained in Microsoft.AspNetCore.Http.Abstractions)
Expand Down
9 changes: 9 additions & 0 deletions src/Http/Http.Extensions/src/TagsAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Shared;

namespace Microsoft.AspNetCore.Http;

Expand All @@ -14,6 +16,7 @@ namespace Microsoft.AspNetCore.Http;
/// and are typically used to group operations by tags in the UI.
/// </remarks>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate | AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[DebuggerDisplay("{ToString(),nq}")]
public sealed class TagsAttribute : Attribute, ITagsMetadata
{
/// <summary>
Expand All @@ -29,4 +32,10 @@ public TagsAttribute(params string[] tags)
/// Gets the collection of tags associated with the endpoint.
/// </summary>
public IReadOnlyList<string> Tags { get; }

/// <inheritdoc/>>
public override string ToString()
{
return DebuggerHelpers.GetDebugText(nameof(Tags), Tags);
}
}
48 changes: 48 additions & 0 deletions src/Http/Http.Extensions/test/MetadataTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.Routing;

public class MetadataTest
{
[Fact]
public void EndpointDescriptionAttribute_ToString()
{
// Arrange
var metadata = new EndpointDescriptionAttribute("A description");

// Act
var value = metadata.ToString();

// Assert
Assert.Equal("Description: A description", value);
}

[Fact]
public void EndpointSummaryAttribute_ToString()
{
// Arrange
var metadata = new EndpointSummaryAttribute("A summary");

// Act
var value = metadata.ToString();

// Assert
Assert.Equal("Summary: A summary", value);
}

[Fact]
public void HostAttribute_ToString()
{
// Arrange
var metadata = new TagsAttribute("Tag1", "Tag2");

// Act
var value = metadata.ToString();

// Assert
Assert.Equal("Tags: Tag1,Tag2", value);
}
}
12 changes: 10 additions & 2 deletions src/Http/Routing/src/DataTokensMetadata.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Shared;

namespace Microsoft.AspNetCore.Routing;

Expand All @@ -12,6 +13,7 @@ namespace Microsoft.AspNetCore.Routing;
/// type provides data tokens value for <see cref="RouteData.DataTokens"/> associated
/// with an endpoint.
/// </summary>
[DebuggerDisplay("{ToString(),nq}")]
public sealed class DataTokensMetadata : IDataTokensMetadata
{
/// <summary>
Expand All @@ -27,4 +29,10 @@ public DataTokensMetadata(IReadOnlyDictionary<string, object?> dataTokens)
/// Get the data tokens.
/// </summary>
public IReadOnlyDictionary<string, object?> DataTokens { get; }

/// <inheritdoc/>
public override string ToString()
{
return DebuggerHelpers.GetDebugText(nameof(DataTokens), DataTokens.Select(t => $"{t.Key}={t.Value ?? "(null)"}"));
}
}
9 changes: 8 additions & 1 deletion src/Http/Routing/src/EndpointNameMetadata.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Shared;

namespace Microsoft.AspNetCore.Routing;

Expand Down Expand Up @@ -29,4 +30,10 @@ public EndpointNameMetadata(string endpointName)
/// Gets the endpoint name.
/// </summary>
public string EndpointName { get; }

/// <inheritdoc/>
public override string ToString()
{
return DebuggerHelpers.GetDebugText(nameof(EndpointName), EndpointName);
}
}
11 changes: 10 additions & 1 deletion src/Http/Routing/src/ExcludeFromDescriptionAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Shared;

namespace Microsoft.AspNetCore.Routing;

/// <summary>
/// Indicates that this <see cref="Endpoint"/> should not be included in the generated API metadata.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate, AllowMultiple = false, Inherited = true)]
[DebuggerDisplay("{ToString(),nq}")]
public sealed class ExcludeFromDescriptionAttribute : Attribute, IExcludeFromDescriptionMetadata
{
/// <inheritdoc />
public bool ExcludeFromDescription => true;

/// <inheritdoc/>>
public override string ToString()
{
return DebuggerHelpers.GetDebugText(nameof(ExcludeFromDescription), ExcludeFromDescription);
}
}
8 changes: 5 additions & 3 deletions src/Http/Routing/src/HostAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@

using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Shared;

namespace Microsoft.AspNetCore.Routing;

/// <summary>
/// Attribute for providing host metdata that is used during routing.
/// </summary>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerDisplay("{ToString(),nq}")]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class HostAttribute : Attribute, IHostMetadata
{
Expand Down Expand Up @@ -47,12 +48,13 @@ public HostAttribute(params string[] hosts)
/// </summary>
public IReadOnlyList<string> Hosts { get; }

private string DebuggerToString()
/// <inheritdoc/>
public override string ToString()
{
var hostsDisplay = (Hosts.Count == 0)
? "*:*"
: string.Join(",", Hosts.Select(h => h.Contains(':') ? h : h + ":*"));

return $"Hosts: {hostsDisplay}";
return DebuggerHelpers.GetDebugText(nameof(Hosts), hostsDisplay);
}
}
8 changes: 5 additions & 3 deletions src/Http/Routing/src/HttpMethodMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Shared;
using static Microsoft.AspNetCore.Http.HttpMethods;

namespace Microsoft.AspNetCore.Routing;

/// <summary>
/// Represents HTTP method metadata used during routing.
/// </summary>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerDisplay("{ToString(),nq}")]
public sealed class HttpMethodMetadata : IHttpMethodMetadata
{
/// <summary>
Expand Down Expand Up @@ -52,8 +53,9 @@ public HttpMethodMetadata(IEnumerable<string> httpMethods, bool acceptCorsPrefli
/// </summary>
public IReadOnlyList<string> HttpMethods { get; }

private string DebuggerToString()
/// <inheritdoc/>
public override string ToString()
{
return $"HttpMethods: {string.Join(",", HttpMethods)} - Cors: {AcceptCorsPreflight}";
return DebuggerHelpers.GetDebugText(nameof(HttpMethods), HttpMethods, "Cors", AcceptCorsPreflight);
}
}
1 change: 1 addition & 0 deletions src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<Compile Include="$(SharedSourceRoot)RouteValueDictionaryTrimmerWarning.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)HttpRuleParser.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)HttpParseResult.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)Debugger\DebuggerHelpers.cs" LinkBase="Shared" />
</ItemGroup>

<ItemGroup>
Expand Down
8 changes: 8 additions & 0 deletions src/Http/Routing/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#nullable enable
Microsoft.AspNetCore.Routing.RouteHandlerServices
override Microsoft.AspNetCore.Routing.DataTokensMetadata.ToString() -> string!
override Microsoft.AspNetCore.Routing.EndpointNameMetadata.ToString() -> string!
override Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute.ToString() -> string!
override Microsoft.AspNetCore.Routing.HostAttribute.ToString() -> string!
override Microsoft.AspNetCore.Routing.HttpMethodMetadata.ToString() -> string!
override Microsoft.AspNetCore.Routing.RouteNameMetadata.ToString() -> string!
override Microsoft.AspNetCore.Routing.SuppressLinkGenerationMetadata.ToString() -> string!
override Microsoft.AspNetCore.Routing.SuppressMatchingMetadata.ToString() -> string!
Microsoft.AspNetCore.Builder.RouteShortCircuitEndpointConventionBuilderExtensions
Microsoft.AspNetCore.Routing.RouteShortCircuitEndpointRouteBuilderExtensions
static Microsoft.AspNetCore.Builder.RouteShortCircuitEndpointConventionBuilderExtensions.ShortCircuit(this Microsoft.AspNetCore.Builder.IEndpointConventionBuilder! builder, int? statusCode = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder!
Expand Down
Loading