Add test suite to analyze ASP.NET Core routing scenarios (#4950)
This commit is contained in:
parent
f25ff3a4f4
commit
0a989a942f
|
|
@ -98,13 +98,16 @@
|
|||
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
|
||||
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="6.0.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.21" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="7.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="7.0.9" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
||||
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="8.0.0-rc.2.23480.2" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.0-rc.2.23480.2" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.0-rc.2.23480.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
|
|
@ -36,4 +37,11 @@
|
|||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\InMemoryEventListener.cs" Link="Includes\InMemoryEventListener.cs" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestEventListener.cs" Link="Includes\TestEventListener.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="RouteTests\RoutingTestCases.json">
|
||||
<LogicalName>RoutingTestCases.json</LogicalName>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,204 @@
|
|||
# ASP.NET Core `http.route` tests
|
||||
|
||||
This folder contains a test suite that validates the instrumentation produces
|
||||
the expected `http.route` attribute on both the activity and metric it emits.
|
||||
When available, the `http.route` is also a required component of the
|
||||
`Activity.DisplayName`.
|
||||
|
||||
The test suite covers a variety of different routing scenarios available for
|
||||
ASP.NET Core:
|
||||
|
||||
* [Conventional routing](https://learn.microsoft.com/aspnet/core/mvc/controllers/routing#conventional-routing)
|
||||
* [Conventional routing using areas](https://learn.microsoft.com/aspnet/core/mvc/controllers/routing#areas)
|
||||
* [Attribute routing](https://learn.microsoft.com/aspnet/core/mvc/controllers/routing#attribute-routing-for-rest-apis)
|
||||
* [Razor pages](https://learn.microsoft.com/aspnet/core/razor-pages/razor-pages-conventions)
|
||||
* [Minimal APIs](https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis/route-handlers)
|
||||
|
||||
The individual test cases are defined in RoutingTestCases.json.
|
||||
|
||||
The test suite is unique in that, when run, it generates README files for each
|
||||
target framework which aids in documenting how the instrumentation behaves for
|
||||
each test case. These files are source-controlled, so if the behavior of the
|
||||
instrumentation changes, the README files will be updated to reflect the change.
|
||||
|
||||
* [.NET 6](./README.net6.0.md)
|
||||
* [.NET 7](./README.net7.0.md)
|
||||
* [.NET 8](./README.net8.0.md)
|
||||
|
||||
For each test case a request is made to an ASP.NET Core application with a
|
||||
particular routing configuration. ASP.NET Core offers a
|
||||
[variety of APIs](#aspnet-core-apis-for-retrieving-route-information) for
|
||||
retrieving the route information of a given request. The README files include
|
||||
detailed information documenting the route information available using the
|
||||
various APIs in each test case. For example, here is the detailed result
|
||||
generated for a test case:
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}",
|
||||
"ActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter/2",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/ActionWithStringParameter/2?num=3",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter",
|
||||
"id": "2"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The test result currently includes an `IdealHttpRoute` property. This is
|
||||
> temporary, and is meant to drive a conversation to determine the best way
|
||||
> for generating the `http.route` attribute under different routing scenarios.
|
||||
> In the example above, the path invoked is
|
||||
> `/ConventionalRoute/ActionWithStringParameter/2?num=3`. Currently, we see
|
||||
> that the `http.route` attribute on the metric emitted is
|
||||
> `{controller=ConventionalRoute}/{action=Default}/{id?}` which was derived
|
||||
> using `RoutePattern.RawText`. This is not ideal
|
||||
> because the route template does not include the actual action that was
|
||||
> invoked `ActionWithStringParameter`. The invoked action could be derived
|
||||
> using either the `ControllerActionDescriptor`
|
||||
> or `HttpContext.GetRouteData()`.
|
||||
|
||||
## ASP.NET Core APIs for retrieving route information
|
||||
|
||||
Included below are short snippets illustrating the use of the various
|
||||
APIs available for retrieving route information.
|
||||
|
||||
### Retrieving the route template
|
||||
|
||||
The route template can be obtained from `HttpContext` by retrieving the
|
||||
`RouteEndpoint` using the following two APIs.
|
||||
|
||||
For attribute routing and minimal API scenarios, using the route template alone
|
||||
is sufficient for deriving `http.route` in all test cases.
|
||||
|
||||
The route template does not well describe the `http.route` in conventional
|
||||
routing and some Razor page scenarios.
|
||||
|
||||
#### [RoutePattern.RawText](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.routing.patterns.routepattern.rawtext)
|
||||
|
||||
```csharp
|
||||
(httpContext.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText;
|
||||
```
|
||||
|
||||
#### [IRouteDiagnosticsMetadata.Route](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.metadata.iroutediagnosticsmetadata.route)
|
||||
|
||||
This API was introduced in .NET 8.
|
||||
|
||||
```csharp
|
||||
httpContext.GetEndpoint()?.Metadata.GetMetadata<IRouteDiagnosticsMetadata>()?.Route;
|
||||
```
|
||||
|
||||
### RouteData
|
||||
|
||||
`RouteData` can be retrieved from `HttpContext` using the `GetRouteData()`
|
||||
extension method. The values obtained from `RouteData` identify the controller/
|
||||
action or Razor page invoked by the request.
|
||||
|
||||
#### [HttpContext.GetRouteData()](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.routing.routinghttpcontextextensions.getroutedata)
|
||||
|
||||
```csharp
|
||||
foreach (var value in httpContext.GetRouteData().Values)
|
||||
{
|
||||
Console.WriteLine($"{value.Key} = {value.Value?.ToString()}");
|
||||
}
|
||||
```
|
||||
|
||||
For example, the above code produces something like:
|
||||
|
||||
```text
|
||||
controller = ConventionalRoute
|
||||
action = ActionWithStringParameter
|
||||
id = 2
|
||||
```
|
||||
|
||||
### Information from the ActionDescriptor
|
||||
|
||||
For requests that invoke an action or Razor page, the `ActionDescriptor` can
|
||||
be used to access route information.
|
||||
|
||||
#### [AttributeRouteInfo.Template](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.routing.attributerouteinfo.template)
|
||||
|
||||
The `AttributeRouteInfo.Template` is equivalent to using
|
||||
[other APIs for retrieving the route template](#retrieving-the-route-template)
|
||||
when using attribute routing. For conventional routing and Razor pages it will
|
||||
be `null`.
|
||||
|
||||
```csharp
|
||||
actionDescriptor.AttributeRouteInfo?.Template;
|
||||
```
|
||||
|
||||
#### [ControllerActionDescriptor](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.controllers.controlleractiondescriptor)
|
||||
|
||||
For requests that invoke an action on a controller, the `ActionDescriptor`
|
||||
will be of type `ControllerActionDescriptor` which includes the controller and
|
||||
action name.
|
||||
|
||||
```csharp
|
||||
(actionDescriptor as ControllerActionDescriptor)?.ControllerName;
|
||||
(actionDescriptor as ControllerActionDescriptor)?.ActionName;
|
||||
```
|
||||
|
||||
#### [PageActionDescriptor](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.razorpages.pageactiondescriptor)
|
||||
|
||||
For requests that invoke a Razor page, the `ActionDescriptor`
|
||||
will be of type `PageActionDescriptor` which includes the path to the invoked
|
||||
page.
|
||||
|
||||
```csharp
|
||||
(actionDescriptor as PageActionDescriptor)?.RelativePath;
|
||||
(actionDescriptor as PageActionDescriptor)?.ViewEnginePath;
|
||||
```
|
||||
|
||||
#### [Parameters](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.abstractions.actiondescriptor.parameters#microsoft-aspnetcore-mvc-abstractions-actiondescriptor-parameters)
|
||||
|
||||
The `ActionDescriptor.Parameters` property is interesting because it describes
|
||||
the actual parameters (type and name) of an invoked action method. Some APM
|
||||
products use `ActionDescriptor.Parameters` to more precisely describe the
|
||||
method an endpoint invokes since not all parameters may be present in the
|
||||
route template.
|
||||
|
||||
Consider the following action method:
|
||||
|
||||
```csharp
|
||||
public IActionResult SomeActionMethod(string id, int num) { ... }
|
||||
```
|
||||
|
||||
Using conventional routing assuming a default route template
|
||||
`{controller=ConventionalRoute}/{action=Default}/{id?}`, the `SomeActionMethod`
|
||||
may match this route template. The route template describes the `id` parameter
|
||||
but not the `num` parameter.
|
||||
|
||||
```csharp
|
||||
foreach (var parameter in actionDescriptor.Parameters)
|
||||
{
|
||||
Console.WriteLine($"{parameter.Name}");
|
||||
}
|
||||
```
|
||||
|
||||
The above code produces:
|
||||
|
||||
```text
|
||||
id
|
||||
num
|
||||
```
|
||||
|
|
@ -0,0 +1,592 @@
|
|||
# Test results for ASP.NET Core 6
|
||||
|
||||
| Span http.route | Metric http.route | App | Test Name |
|
||||
| - | - | - | - |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Root path](#conventionalrouting-root-path) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Non-default action with route parameter and query string](#conventionalrouting-non-default-action-with-route-parameter-and-query-string) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Non-default action with query string](#conventionalrouting-non-default-action-with-query-string) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Not Found (404)](#conventionalrouting-not-found-404) |
|
||||
| :broken_heart: | :green_heart: | ConventionalRouting | [Route template with parameter constraint](#conventionalrouting-route-template-with-parameter-constraint) |
|
||||
| :broken_heart: | :green_heart: | ConventionalRouting | [Path that does not match parameter constraint](#conventionalrouting-path-that-does-not-match-parameter-constraint) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area using area:exists, default controller/action](#conventionalrouting-area-using-areaexists-default-controlleraction) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area using area:exists, non-default action](#conventionalrouting-area-using-areaexists-non-default-action) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area w/o area:exists, default controller/action](#conventionalrouting-area-wo-areaexists-default-controlleraction) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Default action](#attributerouting-default-action) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action without parameter](#attributerouting-action-without-parameter) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action with parameter](#attributerouting-action-with-parameter) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action with parameter before action name in template](#attributerouting-action-with-parameter-before-action-name-in-template) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action invoked resulting in 400 Bad Request](#attributerouting-action-invoked-resulting-in-400-bad-request) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Root path](#razorpages-root-path) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Index page](#razorpages-index-page) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Throws exception](#razorpages-throws-exception) |
|
||||
| :green_heart: | :green_heart: | RazorPages | [Static content](#razorpages-static-content) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action without parameter](#minimalapi-action-without-parameter) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action with parameter](#minimalapi-action-with-parameter) |
|
||||
|
||||
## ConventionalRouting: Root path
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/Default/{id?}",
|
||||
"ActivityDisplayName": "/",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "Default"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "Default"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Non-default action with route parameter and query string
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}",
|
||||
"ActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter/2",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/ActionWithStringParameter/2?num=3",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter",
|
||||
"id": "2"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Non-default action with query string
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}",
|
||||
"ActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/ActionWithStringParameter?num=3",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Not Found (404)
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/ConventionalRoute/NotFound",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/NotFound",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Route template with parameter constraint
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "SomePath/{id}/{num:int}",
|
||||
"ActivityDisplayName": "/SomePath/SomeString/2",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "SomePath/{id}/{num:int}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePath/SomeString/2",
|
||||
"RoutePattern.RawText": "SomePath/{id}/{num:int}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter",
|
||||
"id": "SomeString",
|
||||
"num": "2"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Path that does not match parameter constraint
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/SomePath/SomeString/NotAnInt",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePath/SomeString/NotAnInt",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area using area:exists, default controller/action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "{area:exists}/ControllerForMyArea/Default/{id?}",
|
||||
"ActivityDisplayName": "/MyArea",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MyArea",
|
||||
"RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ControllerForMyArea",
|
||||
"action": "Default",
|
||||
"area": "MyArea"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ControllerForMyArea",
|
||||
"ActionName": "Default"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area using area:exists, non-default action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "{area:exists}/ControllerForMyArea/NonDefault/{id?}",
|
||||
"ActivityDisplayName": "/MyArea/ControllerForMyArea/NonDefault",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MyArea/ControllerForMyArea/NonDefault",
|
||||
"RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ControllerForMyArea",
|
||||
"area": "MyArea",
|
||||
"action": "NonDefault"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ControllerForMyArea",
|
||||
"ActionName": "NonDefault"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area w/o area:exists, default controller/action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "SomePrefix/AnotherArea/Index/{id?}",
|
||||
"ActivityDisplayName": "/SomePrefix",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePrefix",
|
||||
"RoutePattern.RawText": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"area": "AnotherArea",
|
||||
"controller": "AnotherArea",
|
||||
"action": "Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AnotherArea",
|
||||
"ActionName": "Index"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Default action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute",
|
||||
"ActivityDisplayName": "AttributeRoute",
|
||||
"ActivityHttpRoute": "AttributeRoute",
|
||||
"MetricHttpRoute": "AttributeRoute",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute",
|
||||
"RoutePattern.RawText": "AttributeRoute",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action without parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/Get",
|
||||
"ActivityDisplayName": "AttributeRoute/Get",
|
||||
"ActivityHttpRoute": "AttributeRoute/Get",
|
||||
"MetricHttpRoute": "AttributeRoute/Get",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/Get",
|
||||
"RoutePattern.RawText": "AttributeRoute/Get",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/Get",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action with parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"ActivityDisplayName": "AttributeRoute/Get/{id}",
|
||||
"ActivityHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"MetricHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/Get/12",
|
||||
"RoutePattern.RawText": "AttributeRoute/Get/{id}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "12"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/Get/{id}",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action with parameter before action name in template
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityDisplayName": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/12/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "GetWithActionNameInDifferentSpotInTemplate",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "12"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "GetWithActionNameInDifferentSpotInTemplate"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action invoked resulting in 400 Bad Request
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityDisplayName": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/NotAnInt/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "GetWithActionNameInDifferentSpotInTemplate",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "NotAnInt"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "GetWithActionNameInDifferentSpotInTemplate"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Root path
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/Index",
|
||||
"ActivityDisplayName": "/",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/",
|
||||
"RoutePattern.RawText": "",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/Index.cshtml",
|
||||
"ViewEnginePath": "/Index"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Index page
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/Index",
|
||||
"ActivityDisplayName": "Index",
|
||||
"ActivityHttpRoute": "Index",
|
||||
"MetricHttpRoute": "Index",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/Index",
|
||||
"RoutePattern.RawText": "Index",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "Index",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/Index.cshtml",
|
||||
"ViewEnginePath": "/Index"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Throws exception
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/PageThatThrowsException",
|
||||
"ActivityDisplayName": "PageThatThrowsException",
|
||||
"ActivityHttpRoute": "PageThatThrowsException",
|
||||
"MetricHttpRoute": "PageThatThrowsException",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/PageThatThrowsException",
|
||||
"RoutePattern.RawText": "PageThatThrowsException",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/PageThatThrowsException"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "PageThatThrowsException",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/PageThatThrowsException.cshtml",
|
||||
"ViewEnginePath": "/PageThatThrowsException"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Static content
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/js/site.js",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/js/site.js",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action without parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApi",
|
||||
"ActivityDisplayName": "/MinimalApi",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApi",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApi",
|
||||
"RoutePattern.RawText": "/MinimalApi",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action with parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApi/{id}",
|
||||
"ActivityDisplayName": "/MinimalApi/123",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApi/{id}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApi/123",
|
||||
"RoutePattern.RawText": "/MinimalApi/{id}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"id": "123"
|
||||
},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,634 @@
|
|||
# Test results for ASP.NET Core 7
|
||||
|
||||
| Span http.route | Metric http.route | App | Test Name |
|
||||
| - | - | - | - |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Root path](#conventionalrouting-root-path) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Non-default action with route parameter and query string](#conventionalrouting-non-default-action-with-route-parameter-and-query-string) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Non-default action with query string](#conventionalrouting-non-default-action-with-query-string) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Not Found (404)](#conventionalrouting-not-found-404) |
|
||||
| :broken_heart: | :green_heart: | ConventionalRouting | [Route template with parameter constraint](#conventionalrouting-route-template-with-parameter-constraint) |
|
||||
| :broken_heart: | :green_heart: | ConventionalRouting | [Path that does not match parameter constraint](#conventionalrouting-path-that-does-not-match-parameter-constraint) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area using area:exists, default controller/action](#conventionalrouting-area-using-areaexists-default-controlleraction) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area using area:exists, non-default action](#conventionalrouting-area-using-areaexists-non-default-action) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area w/o area:exists, default controller/action](#conventionalrouting-area-wo-areaexists-default-controlleraction) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Default action](#attributerouting-default-action) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action without parameter](#attributerouting-action-without-parameter) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action with parameter](#attributerouting-action-with-parameter) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action with parameter before action name in template](#attributerouting-action-with-parameter-before-action-name-in-template) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action invoked resulting in 400 Bad Request](#attributerouting-action-invoked-resulting-in-400-bad-request) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Root path](#razorpages-root-path) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Index page](#razorpages-index-page) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Throws exception](#razorpages-throws-exception) |
|
||||
| :green_heart: | :green_heart: | RazorPages | [Static content](#razorpages-static-content) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action without parameter](#minimalapi-action-without-parameter) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action with parameter](#minimalapi-action-with-parameter) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action without parameter (MapGroup)](#minimalapi-action-without-parameter-mapgroup) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action with parameter (MapGroup)](#minimalapi-action-with-parameter-mapgroup) |
|
||||
|
||||
## ConventionalRouting: Root path
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/Default/{id?}",
|
||||
"ActivityDisplayName": "/",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "Default"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "Default"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Non-default action with route parameter and query string
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}",
|
||||
"ActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter/2",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/ActionWithStringParameter/2?num=3",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter",
|
||||
"id": "2"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Non-default action with query string
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}",
|
||||
"ActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/ActionWithStringParameter?num=3",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Not Found (404)
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/ConventionalRoute/NotFound",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/NotFound",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Route template with parameter constraint
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "SomePath/{id}/{num:int}",
|
||||
"ActivityDisplayName": "/SomePath/SomeString/2",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "SomePath/{id}/{num:int}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePath/SomeString/2",
|
||||
"RoutePattern.RawText": "SomePath/{id}/{num:int}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter",
|
||||
"id": "SomeString",
|
||||
"num": "2"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Path that does not match parameter constraint
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/SomePath/SomeString/NotAnInt",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePath/SomeString/NotAnInt",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area using area:exists, default controller/action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "{area:exists}/ControllerForMyArea/Default/{id?}",
|
||||
"ActivityDisplayName": "/MyArea",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MyArea",
|
||||
"RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ControllerForMyArea",
|
||||
"action": "Default",
|
||||
"area": "MyArea"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ControllerForMyArea",
|
||||
"ActionName": "Default"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area using area:exists, non-default action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "{area:exists}/ControllerForMyArea/NonDefault/{id?}",
|
||||
"ActivityDisplayName": "/MyArea/ControllerForMyArea/NonDefault",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MyArea/ControllerForMyArea/NonDefault",
|
||||
"RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ControllerForMyArea",
|
||||
"area": "MyArea",
|
||||
"action": "NonDefault"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ControllerForMyArea",
|
||||
"ActionName": "NonDefault"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area w/o area:exists, default controller/action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "SomePrefix/AnotherArea/Index/{id?}",
|
||||
"ActivityDisplayName": "/SomePrefix",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePrefix",
|
||||
"RoutePattern.RawText": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"area": "AnotherArea",
|
||||
"controller": "AnotherArea",
|
||||
"action": "Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AnotherArea",
|
||||
"ActionName": "Index"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Default action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute",
|
||||
"ActivityDisplayName": "AttributeRoute",
|
||||
"ActivityHttpRoute": "AttributeRoute",
|
||||
"MetricHttpRoute": "AttributeRoute",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute",
|
||||
"RoutePattern.RawText": "AttributeRoute",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action without parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/Get",
|
||||
"ActivityDisplayName": "AttributeRoute/Get",
|
||||
"ActivityHttpRoute": "AttributeRoute/Get",
|
||||
"MetricHttpRoute": "AttributeRoute/Get",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/Get",
|
||||
"RoutePattern.RawText": "AttributeRoute/Get",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/Get",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action with parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"ActivityDisplayName": "AttributeRoute/Get/{id}",
|
||||
"ActivityHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"MetricHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/Get/12",
|
||||
"RoutePattern.RawText": "AttributeRoute/Get/{id}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "12"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/Get/{id}",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action with parameter before action name in template
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityDisplayName": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/12/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "GetWithActionNameInDifferentSpotInTemplate",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "12"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "GetWithActionNameInDifferentSpotInTemplate"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action invoked resulting in 400 Bad Request
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityDisplayName": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/NotAnInt/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "GetWithActionNameInDifferentSpotInTemplate",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "NotAnInt"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "GetWithActionNameInDifferentSpotInTemplate"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Root path
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/Index",
|
||||
"ActivityDisplayName": "/",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/",
|
||||
"RoutePattern.RawText": "",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/Index.cshtml",
|
||||
"ViewEnginePath": "/Index"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Index page
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/Index",
|
||||
"ActivityDisplayName": "Index",
|
||||
"ActivityHttpRoute": "Index",
|
||||
"MetricHttpRoute": "Index",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/Index",
|
||||
"RoutePattern.RawText": "Index",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "Index",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/Index.cshtml",
|
||||
"ViewEnginePath": "/Index"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Throws exception
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/PageThatThrowsException",
|
||||
"ActivityDisplayName": "PageThatThrowsException",
|
||||
"ActivityHttpRoute": "PageThatThrowsException",
|
||||
"MetricHttpRoute": "PageThatThrowsException",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/PageThatThrowsException",
|
||||
"RoutePattern.RawText": "PageThatThrowsException",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/PageThatThrowsException"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "PageThatThrowsException",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/PageThatThrowsException.cshtml",
|
||||
"ViewEnginePath": "/PageThatThrowsException"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Static content
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/js/site.js",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/js/site.js",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action without parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApi",
|
||||
"ActivityDisplayName": "/MinimalApi",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApi",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApi",
|
||||
"RoutePattern.RawText": "/MinimalApi",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action with parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApi/{id}",
|
||||
"ActivityDisplayName": "/MinimalApi/123",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApi/{id}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApi/123",
|
||||
"RoutePattern.RawText": "/MinimalApi/{id}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"id": "123"
|
||||
},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action without parameter (MapGroup)
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApiUsingMapGroup/",
|
||||
"ActivityDisplayName": "/MinimalApiUsingMapGroup",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApiUsingMapGroup/",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApiUsingMapGroup",
|
||||
"RoutePattern.RawText": "/MinimalApiUsingMapGroup/",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action with parameter (MapGroup)
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApiUsingMapGroup/{id}",
|
||||
"ActivityDisplayName": "/MinimalApiUsingMapGroup/123",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApiUsingMapGroup/{id}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApiUsingMapGroup/123",
|
||||
"RoutePattern.RawText": "/MinimalApiUsingMapGroup/{id}",
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {
|
||||
"id": "123"
|
||||
},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,634 @@
|
|||
# Test results for ASP.NET Core 8
|
||||
|
||||
| Span http.route | Metric http.route | App | Test Name |
|
||||
| - | - | - | - |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Root path](#conventionalrouting-root-path) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Non-default action with route parameter and query string](#conventionalrouting-non-default-action-with-route-parameter-and-query-string) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Non-default action with query string](#conventionalrouting-non-default-action-with-query-string) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Not Found (404)](#conventionalrouting-not-found-404) |
|
||||
| :broken_heart: | :green_heart: | ConventionalRouting | [Route template with parameter constraint](#conventionalrouting-route-template-with-parameter-constraint) |
|
||||
| :broken_heart: | :green_heart: | ConventionalRouting | [Path that does not match parameter constraint](#conventionalrouting-path-that-does-not-match-parameter-constraint) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area using area:exists, default controller/action](#conventionalrouting-area-using-areaexists-default-controlleraction) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area using area:exists, non-default action](#conventionalrouting-area-using-areaexists-non-default-action) |
|
||||
| :broken_heart: | :broken_heart: | ConventionalRouting | [Area w/o area:exists, default controller/action](#conventionalrouting-area-wo-areaexists-default-controlleraction) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Default action](#attributerouting-default-action) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action without parameter](#attributerouting-action-without-parameter) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action with parameter](#attributerouting-action-with-parameter) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action with parameter before action name in template](#attributerouting-action-with-parameter-before-action-name-in-template) |
|
||||
| :green_heart: | :green_heart: | AttributeRouting | [Action invoked resulting in 400 Bad Request](#attributerouting-action-invoked-resulting-in-400-bad-request) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Root path](#razorpages-root-path) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Index page](#razorpages-index-page) |
|
||||
| :broken_heart: | :broken_heart: | RazorPages | [Throws exception](#razorpages-throws-exception) |
|
||||
| :green_heart: | :green_heart: | RazorPages | [Static content](#razorpages-static-content) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action without parameter](#minimalapi-action-without-parameter) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action with parameter](#minimalapi-action-with-parameter) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action without parameter (MapGroup)](#minimalapi-action-without-parameter-mapgroup) |
|
||||
| :broken_heart: | :green_heart: | MinimalApi | [Action with parameter (MapGroup)](#minimalapi-action-with-parameter-mapgroup) |
|
||||
|
||||
## ConventionalRouting: Root path
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/Default/{id?}",
|
||||
"ActivityDisplayName": "/",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "Default"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "Default"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Non-default action with route parameter and query string
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}",
|
||||
"ActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter/2",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/ActionWithStringParameter/2?num=3",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter",
|
||||
"id": "2"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Non-default action with query string
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}",
|
||||
"ActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/ActionWithStringParameter?num=3",
|
||||
"RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Not Found (404)
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/ConventionalRoute/NotFound",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/ConventionalRoute/NotFound",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Route template with parameter constraint
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "SomePath/{id}/{num:int}",
|
||||
"ActivityDisplayName": "/SomePath/SomeString/2",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "SomePath/{id}/{num:int}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePath/SomeString/2",
|
||||
"RoutePattern.RawText": "SomePath/{id}/{num:int}",
|
||||
"IRouteDiagnosticsMetadata.Route": "SomePath/{id}/{num:int}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ConventionalRoute",
|
||||
"action": "ActionWithStringParameter",
|
||||
"id": "SomeString",
|
||||
"num": "2"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [
|
||||
"id",
|
||||
"num"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ConventionalRoute",
|
||||
"ActionName": "ActionWithStringParameter"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Path that does not match parameter constraint
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/SomePath/SomeString/NotAnInt",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePath/SomeString/NotAnInt",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area using area:exists, default controller/action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "{area:exists}/ControllerForMyArea/Default/{id?}",
|
||||
"ActivityDisplayName": "/MyArea",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MyArea",
|
||||
"RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ControllerForMyArea",
|
||||
"action": "Default",
|
||||
"area": "MyArea"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ControllerForMyArea",
|
||||
"ActionName": "Default"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area using area:exists, non-default action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "{area:exists}/ControllerForMyArea/NonDefault/{id?}",
|
||||
"ActivityDisplayName": "/MyArea/ControllerForMyArea/NonDefault",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MyArea/ControllerForMyArea/NonDefault",
|
||||
"RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"controller": "ControllerForMyArea",
|
||||
"area": "MyArea",
|
||||
"action": "NonDefault"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "ControllerForMyArea",
|
||||
"ActionName": "NonDefault"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ConventionalRouting: Area w/o area:exists, default controller/action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "SomePrefix/AnotherArea/Index/{id?}",
|
||||
"ActivityDisplayName": "/SomePrefix",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/SomePrefix",
|
||||
"RoutePattern.RawText": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}",
|
||||
"IRouteDiagnosticsMetadata.Route": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"area": "AnotherArea",
|
||||
"controller": "AnotherArea",
|
||||
"action": "Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": null,
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AnotherArea",
|
||||
"ActionName": "Index"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Default action
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute",
|
||||
"ActivityDisplayName": "AttributeRoute",
|
||||
"ActivityHttpRoute": "AttributeRoute",
|
||||
"MetricHttpRoute": "AttributeRoute",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute",
|
||||
"RoutePattern.RawText": "AttributeRoute",
|
||||
"IRouteDiagnosticsMetadata.Route": "AttributeRoute",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action without parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/Get",
|
||||
"ActivityDisplayName": "AttributeRoute/Get",
|
||||
"ActivityHttpRoute": "AttributeRoute/Get",
|
||||
"MetricHttpRoute": "AttributeRoute/Get",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/Get",
|
||||
"RoutePattern.RawText": "AttributeRoute/Get",
|
||||
"IRouteDiagnosticsMetadata.Route": "AttributeRoute/Get",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/Get",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action with parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"ActivityDisplayName": "AttributeRoute/Get/{id}",
|
||||
"ActivityHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"MetricHttpRoute": "AttributeRoute/Get/{id}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/Get/12",
|
||||
"RoutePattern.RawText": "AttributeRoute/Get/{id}",
|
||||
"IRouteDiagnosticsMetadata.Route": "AttributeRoute/Get/{id}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "Get",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "12"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/Get/{id}",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "Get"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action with parameter before action name in template
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityDisplayName": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/12/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"IRouteDiagnosticsMetadata.Route": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "GetWithActionNameInDifferentSpotInTemplate",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "12"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "GetWithActionNameInDifferentSpotInTemplate"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## AttributeRouting: Action invoked resulting in 400 Bad Request
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityDisplayName": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/AttributeRoute/NotAnInt/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"IRouteDiagnosticsMetadata.Route": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"action": "GetWithActionNameInDifferentSpotInTemplate",
|
||||
"controller": "AttributeRoute",
|
||||
"id": "NotAnInt"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"Parameters": [
|
||||
"id"
|
||||
],
|
||||
"ControllerActionDescriptor": {
|
||||
"ControllerName": "AttributeRoute",
|
||||
"ActionName": "GetWithActionNameInDifferentSpotInTemplate"
|
||||
},
|
||||
"PageActionDescriptor": null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Root path
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/Index",
|
||||
"ActivityDisplayName": "/",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/",
|
||||
"RoutePattern.RawText": "",
|
||||
"IRouteDiagnosticsMetadata.Route": "",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/Index.cshtml",
|
||||
"ViewEnginePath": "/Index"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Index page
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/Index",
|
||||
"ActivityDisplayName": "Index",
|
||||
"ActivityHttpRoute": "Index",
|
||||
"MetricHttpRoute": "Index",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/Index",
|
||||
"RoutePattern.RawText": "Index",
|
||||
"IRouteDiagnosticsMetadata.Route": "Index",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/Index"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "Index",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/Index.cshtml",
|
||||
"ViewEnginePath": "/Index"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Throws exception
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/PageThatThrowsException",
|
||||
"ActivityDisplayName": "PageThatThrowsException",
|
||||
"ActivityHttpRoute": "PageThatThrowsException",
|
||||
"MetricHttpRoute": "PageThatThrowsException",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/PageThatThrowsException",
|
||||
"RoutePattern.RawText": "PageThatThrowsException",
|
||||
"IRouteDiagnosticsMetadata.Route": "PageThatThrowsException",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"page": "/PageThatThrowsException"
|
||||
},
|
||||
"ActionDescriptor": {
|
||||
"AttributeRouteInfo.Template": "PageThatThrowsException",
|
||||
"Parameters": [],
|
||||
"ControllerActionDescriptor": null,
|
||||
"PageActionDescriptor": {
|
||||
"RelativePath": "/Pages/PageThatThrowsException.cshtml",
|
||||
"ViewEnginePath": "/PageThatThrowsException"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## RazorPages: Static content
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "",
|
||||
"ActivityDisplayName": "/js/site.js",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/js/site.js",
|
||||
"RoutePattern.RawText": null,
|
||||
"IRouteDiagnosticsMetadata.Route": null,
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action without parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApi",
|
||||
"ActivityDisplayName": "/MinimalApi",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApi",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApi",
|
||||
"RoutePattern.RawText": "/MinimalApi",
|
||||
"IRouteDiagnosticsMetadata.Route": "/MinimalApi",
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action with parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApi/{id}",
|
||||
"ActivityDisplayName": "/MinimalApi/123",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApi/{id}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApi/123",
|
||||
"RoutePattern.RawText": "/MinimalApi/{id}",
|
||||
"IRouteDiagnosticsMetadata.Route": "/MinimalApi/{id}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"id": "123"
|
||||
},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action without parameter (MapGroup)
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApiUsingMapGroup/",
|
||||
"ActivityDisplayName": "/MinimalApiUsingMapGroup",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApiUsingMapGroup/",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApiUsingMapGroup",
|
||||
"RoutePattern.RawText": "/MinimalApiUsingMapGroup/",
|
||||
"IRouteDiagnosticsMetadata.Route": "/MinimalApiUsingMapGroup/",
|
||||
"HttpContext.GetRouteData()": {},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## MinimalApi: Action with parameter (MapGroup)
|
||||
|
||||
```json
|
||||
{
|
||||
"IdealHttpRoute": "/MinimalApiUsingMapGroup/{id}",
|
||||
"ActivityDisplayName": "/MinimalApiUsingMapGroup/123",
|
||||
"ActivityHttpRoute": "",
|
||||
"MetricHttpRoute": "/MinimalApiUsingMapGroup/{id}",
|
||||
"RouteInfo": {
|
||||
"HttpMethod": "GET",
|
||||
"Path": "/MinimalApiUsingMapGroup/123",
|
||||
"RoutePattern.RawText": "/MinimalApiUsingMapGroup/{id}",
|
||||
"IRouteDiagnosticsMetadata.Route": "/MinimalApiUsingMapGroup/{id}",
|
||||
"HttpContext.GetRouteData()": {
|
||||
"id": "123"
|
||||
},
|
||||
"ActionDescriptor": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// <copyright file="RoutingTestCases.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using RouteTests.TestApplication;
|
||||
|
||||
namespace RouteTests;
|
||||
|
||||
public static class RoutingTestCases
|
||||
{
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var input = JsonSerializer.Deserialize<TestCase[]>(
|
||||
assembly.GetManifestResourceStream("RoutingTestCases.json")!,
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
Converters = { new JsonStringEnumConverter() },
|
||||
});
|
||||
return GetArgumentsFromTestCaseObject(input!);
|
||||
}
|
||||
|
||||
private static IEnumerable<object[]> GetArgumentsFromTestCaseObject(IEnumerable<TestCase> input)
|
||||
{
|
||||
var result = new List<object[]>();
|
||||
|
||||
foreach (var testCase in input)
|
||||
{
|
||||
if (testCase.MinimumDotnetVersion.HasValue && Environment.Version.Major < testCase.MinimumDotnetVersion.Value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(new object[] { testCase, true });
|
||||
result.Add(new object[] { testCase, false });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public class TestCase
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public int? MinimumDotnetVersion { get; set; }
|
||||
|
||||
public TestApplicationScenario TestApplicationScenario { get; set; }
|
||||
|
||||
public string? HttpMethod { get; set; }
|
||||
|
||||
public string Path { get; set; } = string.Empty;
|
||||
|
||||
public int ExpectedStatusCode { get; set; }
|
||||
|
||||
public string? ExpectedHttpRoute { get; set; }
|
||||
|
||||
public string? CurrentActivityDisplayName { get; set; }
|
||||
|
||||
public string? CurrentActivityHttpRoute { get; set; }
|
||||
|
||||
public string? CurrentMetricHttpRoute { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// This is used by Visual Studio's test runner to identify the test case.
|
||||
return $"{this.TestApplicationScenario}: {this.Name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
[
|
||||
{
|
||||
"name": "Root path",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"expectedHttpRoute": "ConventionalRoute/Default/{id?}"
|
||||
},
|
||||
{
|
||||
"name": "Non-default action with route parameter and query string",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/ConventionalRoute/ActionWithStringParameter/2?num=3",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter/2",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"expectedHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}"
|
||||
},
|
||||
{
|
||||
"name": "Non-default action with query string",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/ConventionalRoute/ActionWithStringParameter?num=3",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}",
|
||||
"expectedHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}"
|
||||
},
|
||||
{
|
||||
"name": "Not Found (404)",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/ConventionalRoute/NotFound",
|
||||
"expectedStatusCode": 404,
|
||||
"currentActivityDisplayName": "/ConventionalRoute/NotFound",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": "",
|
||||
"expectedHttpRoute": ""
|
||||
},
|
||||
{
|
||||
"name": "Route template with parameter constraint",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/SomePath/SomeString/2",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/SomePath/SomeString/2",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "SomePath/{id}/{num:int}"
|
||||
},
|
||||
{
|
||||
"name": "Path that does not match parameter constraint",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/SomePath/SomeString/NotAnInt",
|
||||
"expectedStatusCode": 404,
|
||||
"currentActivityDisplayName": "/SomePath/SomeString/NotAnInt",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": ""
|
||||
},
|
||||
{
|
||||
"name": "Area using area:exists, default controller/action",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/MyArea",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/MyArea",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"expectedHttpRoute": "{area:exists}/ControllerForMyArea/Default/{id?}"
|
||||
},
|
||||
{
|
||||
"name": "Area using area:exists, non-default action",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/MyArea/ControllerForMyArea/NonDefault",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/MyArea/ControllerForMyArea/NonDefault",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}",
|
||||
"expectedHttpRoute": "{area:exists}/ControllerForMyArea/NonDefault/{id?}"
|
||||
},
|
||||
{
|
||||
"name": "Area w/o area:exists, default controller/action",
|
||||
"testApplicationScenario": "ConventionalRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/SomePrefix",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/SomePrefix",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}",
|
||||
"expectedHttpRoute": "SomePrefix/AnotherArea/Index/{id?}"
|
||||
},
|
||||
{
|
||||
"name": "Default action",
|
||||
"testApplicationScenario": "AttributeRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/AttributeRoute",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "AttributeRoute",
|
||||
"currentActivityHttpRoute": null,
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "AttributeRoute"
|
||||
},
|
||||
{
|
||||
"name": "Action without parameter",
|
||||
"testApplicationScenario": "AttributeRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/AttributeRoute/Get",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "AttributeRoute/Get",
|
||||
"currentActivityHttpRoute": null,
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "AttributeRoute/Get"
|
||||
},
|
||||
{
|
||||
"name": "Action with parameter",
|
||||
"testApplicationScenario": "AttributeRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/AttributeRoute/Get/12",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "AttributeRoute/Get/{id}",
|
||||
"currentActivityHttpRoute": null,
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "AttributeRoute/Get/{id}"
|
||||
},
|
||||
{
|
||||
"name": "Action with parameter before action name in template",
|
||||
"testApplicationScenario": "AttributeRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/AttributeRoute/12/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"currentActivityHttpRoute": null,
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate"
|
||||
},
|
||||
{
|
||||
"name": "Action invoked resulting in 400 Bad Request",
|
||||
"testApplicationScenario": "AttributeRouting",
|
||||
"httpMethod": "GET",
|
||||
"path": "/AttributeRoute/NotAnInt/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"expectedStatusCode": 400,
|
||||
"currentActivityDisplayName": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate",
|
||||
"currentActivityHttpRoute": null,
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate"
|
||||
},
|
||||
{
|
||||
"name": "Root path",
|
||||
"testApplicationScenario": "RazorPages",
|
||||
"httpMethod": "GET",
|
||||
"path": "/",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": "",
|
||||
"expectedHttpRoute": "/Index"
|
||||
},
|
||||
{
|
||||
"name": "Index page",
|
||||
"testApplicationScenario": "RazorPages",
|
||||
"httpMethod": "GET",
|
||||
"path": "/Index",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "Index",
|
||||
"currentActivityHttpRoute": "Index",
|
||||
"currentMetricHttpRoute": "Index",
|
||||
"expectedHttpRoute": "/Index"
|
||||
},
|
||||
{
|
||||
"name": "Throws exception",
|
||||
"testApplicationScenario": "RazorPages",
|
||||
"httpMethod": "GET",
|
||||
"path": "/PageThatThrowsException",
|
||||
"expectedStatusCode": 500,
|
||||
"currentActivityDisplayName": "PageThatThrowsException",
|
||||
"currentActivityHttpRoute": "PageThatThrowsException",
|
||||
"currentMetricHttpRoute": "PageThatThrowsException",
|
||||
"expectedHttpRoute": "/PageThatThrowsException"
|
||||
},
|
||||
{
|
||||
"name": "Static content",
|
||||
"testApplicationScenario": "RazorPages",
|
||||
"httpMethod": "GET",
|
||||
"path": "/js/site.js",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/js/site.js",
|
||||
"currentActivityHttpRoute": null,
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": ""
|
||||
},
|
||||
{
|
||||
"name": "Action without parameter",
|
||||
"testApplicationScenario": "MinimalApi",
|
||||
"httpMethod": "GET",
|
||||
"path": "/MinimalApi",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/MinimalApi",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "/MinimalApi"
|
||||
},
|
||||
{
|
||||
"name": "Action with parameter",
|
||||
"testApplicationScenario": "MinimalApi",
|
||||
"httpMethod": "GET",
|
||||
"path": "/MinimalApi/123",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/MinimalApi/123",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "/MinimalApi/{id}"
|
||||
},
|
||||
{
|
||||
"name": "Action without parameter (MapGroup)",
|
||||
"minimumDotnetVersion": 7,
|
||||
"testApplicationScenario": "MinimalApi",
|
||||
"httpMethod": "GET",
|
||||
"path": "/MinimalApiUsingMapGroup",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/MinimalApiUsingMapGroup",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "/MinimalApiUsingMapGroup/"
|
||||
},
|
||||
{
|
||||
"name": "Action with parameter (MapGroup)",
|
||||
"minimumDotnetVersion": 7,
|
||||
"testApplicationScenario": "MinimalApi",
|
||||
"httpMethod": "GET",
|
||||
"path": "/MinimalApiUsingMapGroup/123",
|
||||
"expectedStatusCode": 200,
|
||||
"currentActivityDisplayName": "/MinimalApiUsingMapGroup/123",
|
||||
"currentActivityHttpRoute": "",
|
||||
"currentMetricHttpRoute": null,
|
||||
"expectedHttpRoute": "/MinimalApiUsingMapGroup/{id}"
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
// <copyright file="RoutingTestFixture.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using RouteTests.TestApplication;
|
||||
|
||||
namespace RouteTests;
|
||||
|
||||
public class RoutingTestFixture : IDisposable
|
||||
{
|
||||
private static readonly HttpClient HttpClient = new();
|
||||
private readonly Dictionary<TestApplicationScenario, WebApplication> apps = new();
|
||||
private readonly RouteInfoDiagnosticObserver diagnostics = new();
|
||||
private readonly List<RoutingTestResult> testResults = new();
|
||||
|
||||
public RoutingTestFixture()
|
||||
{
|
||||
foreach (var scenario in Enum.GetValues<TestApplicationScenario>())
|
||||
{
|
||||
var app = TestApplicationFactory.CreateApplication(scenario);
|
||||
if (app != null)
|
||||
{
|
||||
this.apps.Add(scenario, app);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var app in this.apps)
|
||||
{
|
||||
app.Value.RunAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MakeRequest(TestApplicationScenario scenario, string path)
|
||||
{
|
||||
var app = this.apps[scenario];
|
||||
var baseUrl = app.Urls.First();
|
||||
var url = $"{baseUrl}{path}";
|
||||
await HttpClient.GetAsync(url).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void AddTestResult(RoutingTestResult result)
|
||||
{
|
||||
this.testResults.Add(result);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var app in this.apps)
|
||||
{
|
||||
app.Value.DisposeAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
HttpClient.Dispose();
|
||||
this.diagnostics.Dispose();
|
||||
|
||||
this.GenerateReadme();
|
||||
}
|
||||
|
||||
private void GenerateReadme()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"# Test results for ASP.NET Core {Environment.Version.Major}");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("| Span http.route | Metric http.route | App | Test Name |");
|
||||
sb.AppendLine("| - | - | - | - |");
|
||||
|
||||
for (var i = 0; i < this.testResults.Count; ++i)
|
||||
{
|
||||
var result = this.testResults[i];
|
||||
var emoji1 = result.TestCase.CurrentActivityHttpRoute == null ? ":green_heart:" : ":broken_heart:";
|
||||
var emoji2 = result.TestCase.CurrentMetricHttpRoute == null ? ":green_heart:" : ":broken_heart:";
|
||||
sb.Append($"| {emoji1} | {emoji2} ");
|
||||
sb.AppendLine($"| {result.TestCase.TestApplicationScenario} | [{result.TestCase.Name}]({MakeAnchorTag(result.TestCase.TestApplicationScenario, result.TestCase.Name)}) |");
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.testResults.Count; ++i)
|
||||
{
|
||||
var result = this.testResults[i];
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"## {result.TestCase.TestApplicationScenario}: {result.TestCase.Name}");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("```json");
|
||||
sb.AppendLine(result.ToString());
|
||||
sb.AppendLine("```");
|
||||
}
|
||||
|
||||
var readmeFileName = $"README.net{Environment.Version.Major}.0.md";
|
||||
File.WriteAllText(Path.Combine("..", "..", "..", "RouteTests", readmeFileName), sb.ToString());
|
||||
|
||||
static string MakeAnchorTag(TestApplicationScenario scenario, string name)
|
||||
{
|
||||
var chars = name.ToCharArray()
|
||||
.Where(c => !char.IsPunctuation(c) || c == '-')
|
||||
.Select(c => c switch
|
||||
{
|
||||
'-' => '-',
|
||||
' ' => '-',
|
||||
_ => char.ToLower(c),
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
return $"#{scenario.ToString().ToLower()}-{new string(chars)}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// <copyright file="RoutingTestResult.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using RouteTests.TestApplication;
|
||||
|
||||
namespace RouteTests;
|
||||
|
||||
public class RoutingTestResult
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new() { WriteIndented = true };
|
||||
|
||||
public string? IdealHttpRoute { get; set; }
|
||||
|
||||
public string ActivityDisplayName { get; set; } = string.Empty;
|
||||
|
||||
public string? ActivityHttpRoute { get; set; }
|
||||
|
||||
public string? MetricHttpRoute { get; set; }
|
||||
|
||||
public RouteInfo RouteInfo { get; set; } = new RouteInfo();
|
||||
|
||||
[JsonIgnore]
|
||||
public RoutingTestCases.TestCase TestCase { get; set; } = new RoutingTestCases.TestCase();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonSerializer.Serialize(this, JsonSerializerOptions);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
// <copyright file="RoutingTests.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
using RouteTests.TestApplication;
|
||||
using Xunit;
|
||||
using static OpenTelemetry.Internal.HttpSemanticConventionHelper;
|
||||
|
||||
namespace RouteTests;
|
||||
|
||||
public class RoutingTests : IClassFixture<RoutingTestFixture>
|
||||
{
|
||||
private const string OldHttpStatusCode = "http.status_code";
|
||||
private const string OldHttpMethod = "http.method";
|
||||
private const string HttpStatusCode = "http.response.status_code";
|
||||
private const string HttpMethod = "http.request.method";
|
||||
private const string HttpRoute = "http.route";
|
||||
|
||||
private readonly RoutingTestFixture fixture;
|
||||
private readonly List<Activity> exportedActivities = new();
|
||||
private readonly List<Metric> exportedMetrics = new();
|
||||
|
||||
public RoutingTests(RoutingTestFixture fixture)
|
||||
{
|
||||
this.fixture = fixture;
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TestData => RoutingTestCases.GetTestCases();
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(TestData))]
|
||||
public async Task TestHttpRoute(RoutingTestCases.TestCase testCase, bool useLegacyConventions)
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?> { [SemanticConventionOptInKeyName] = useLegacyConventions ? null : "http" })
|
||||
.Build();
|
||||
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.ConfigureServices(services => services.AddSingleton<IConfiguration>(configuration))
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddInMemoryExporter(this.exportedActivities)
|
||||
.Build()!;
|
||||
|
||||
using var meterProvider = Sdk.CreateMeterProviderBuilder()
|
||||
.ConfigureServices(services => services.AddSingleton<IConfiguration>(configuration))
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddInMemoryExporter(this.exportedMetrics)
|
||||
.Build()!;
|
||||
|
||||
await this.fixture.MakeRequest(testCase.TestApplicationScenario, testCase.Path);
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
if (this.exportedActivities.Count > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
meterProvider.ForceFlush();
|
||||
|
||||
var durationMetric = this.exportedMetrics.Single(x => x.Name == "http.server.request.duration" || x.Name == "http.server.duration");
|
||||
var metricPoints = new List<MetricPoint>();
|
||||
foreach (var mp in durationMetric.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
var activity = Assert.Single(this.exportedActivities);
|
||||
var metricPoint = Assert.Single(metricPoints);
|
||||
|
||||
GetTagsFromActivity(useLegacyConventions, activity, out var activityHttpStatusCode, out var activityHttpMethod, out var activityHttpRoute);
|
||||
GetTagsFromMetricPoint(useLegacyConventions && Environment.Version.Major < 8, metricPoint, out var metricHttpStatusCode, out var metricHttpMethod, out var metricHttpRoute);
|
||||
|
||||
Assert.Equal(testCase.ExpectedStatusCode, activityHttpStatusCode);
|
||||
Assert.Equal(testCase.ExpectedStatusCode, metricHttpStatusCode);
|
||||
Assert.Equal(testCase.HttpMethod, activityHttpMethod);
|
||||
Assert.Equal(testCase.HttpMethod, metricHttpMethod);
|
||||
|
||||
// TODO: The CurrentActivityDisplayName, CurrentActivityHttpRoute, and CurrentMetricHttpRoute
|
||||
// properties will go away. They only serve to capture status quo. The "else" blocks are the real
|
||||
// asserts that we ultimately want.
|
||||
// If any of the current properties are null, then that means we already conform to the
|
||||
// correct behavior.
|
||||
if (testCase.CurrentActivityDisplayName != null)
|
||||
{
|
||||
Assert.Equal(testCase.CurrentActivityDisplayName, activity.DisplayName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Activity.DisplayName should be a combination of http.method + http.route attributes, see:
|
||||
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#name
|
||||
var expectedActivityDisplayName = string.IsNullOrEmpty(testCase.ExpectedHttpRoute)
|
||||
? testCase.HttpMethod
|
||||
: $"{testCase.HttpMethod} {testCase.ExpectedHttpRoute}";
|
||||
|
||||
Assert.Equal(expectedActivityDisplayName, activity.DisplayName);
|
||||
}
|
||||
|
||||
if (testCase.CurrentActivityHttpRoute != null)
|
||||
{
|
||||
Assert.Equal(testCase.CurrentActivityHttpRoute, activityHttpRoute);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(testCase.ExpectedHttpRoute, activityHttpRoute);
|
||||
}
|
||||
|
||||
if (testCase.CurrentMetricHttpRoute != null)
|
||||
{
|
||||
Assert.Equal(testCase.CurrentMetricHttpRoute, metricHttpRoute);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(testCase.ExpectedHttpRoute, metricHttpRoute);
|
||||
}
|
||||
|
||||
// Only produce README files based on final semantic conventions
|
||||
if (!useLegacyConventions)
|
||||
{
|
||||
var testResult = new RoutingTestResult
|
||||
{
|
||||
IdealHttpRoute = testCase.ExpectedHttpRoute,
|
||||
ActivityDisplayName = activity.DisplayName,
|
||||
ActivityHttpRoute = activityHttpRoute,
|
||||
MetricHttpRoute = metricHttpRoute,
|
||||
TestCase = testCase,
|
||||
RouteInfo = RouteInfo.Current,
|
||||
};
|
||||
|
||||
this.fixture.AddTestResult(testResult);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetTagsFromActivity(bool useLegacyConventions, Activity activity, out int httpStatusCode, out string httpMethod, out string? httpRoute)
|
||||
{
|
||||
var expectedStatusCodeKey = useLegacyConventions ? OldHttpStatusCode : HttpStatusCode;
|
||||
var expectedHttpMethodKey = useLegacyConventions ? OldHttpMethod : HttpMethod;
|
||||
httpStatusCode = Convert.ToInt32(activity.GetTagItem(expectedStatusCodeKey));
|
||||
httpMethod = (activity.GetTagItem(expectedHttpMethodKey) as string)!;
|
||||
httpRoute = activity.GetTagItem(HttpRoute) as string ?? string.Empty;
|
||||
}
|
||||
|
||||
private static void GetTagsFromMetricPoint(bool useLegacyConventions, MetricPoint metricPoint, out int httpStatusCode, out string httpMethod, out string? httpRoute)
|
||||
{
|
||||
var expectedStatusCodeKey = useLegacyConventions ? OldHttpStatusCode : HttpStatusCode;
|
||||
var expectedHttpMethodKey = useLegacyConventions ? OldHttpMethod : HttpMethod;
|
||||
|
||||
httpStatusCode = 0;
|
||||
httpMethod = string.Empty;
|
||||
httpRoute = string.Empty;
|
||||
|
||||
foreach (var tag in metricPoint.Tags)
|
||||
{
|
||||
if (tag.Key.Equals(expectedStatusCodeKey))
|
||||
{
|
||||
httpStatusCode = Convert.ToInt32(tag.Value);
|
||||
}
|
||||
else if (tag.Key.Equals(expectedHttpMethodKey))
|
||||
{
|
||||
httpMethod = (tag.Value as string)!;
|
||||
}
|
||||
else if (tag.Key.Equals(HttpRoute))
|
||||
{
|
||||
httpRoute = tag.Value as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// <copyright file="AnotherAreaController.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace RouteTests.Controllers;
|
||||
|
||||
[Area("AnotherArea")]
|
||||
public class AnotherAreaController : Controller
|
||||
{
|
||||
public IActionResult Index() => this.Ok();
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// <copyright file="ControllerForMyAreaController.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace RouteTests.Controllers;
|
||||
|
||||
[Area("MyArea")]
|
||||
public class ControllerForMyAreaController : Controller
|
||||
{
|
||||
public IActionResult Default() => this.Ok();
|
||||
|
||||
public IActionResult NonDefault() => this.Ok();
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// <copyright file="AttributeRouteController.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace RouteTests.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class AttributeRouteController : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
[HttpGet("[action]")]
|
||||
public IActionResult Get() => this.Ok();
|
||||
|
||||
[HttpGet("[action]/{id}")]
|
||||
public IActionResult Get(int id) => this.Ok();
|
||||
|
||||
[HttpGet("{id}/[action]")]
|
||||
public IActionResult GetWithActionNameInDifferentSpotInTemplate(int id) => this.Ok();
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// <copyright file="ConventionalRouteController.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace RouteTests.Controllers;
|
||||
|
||||
public class ConventionalRouteController : Controller
|
||||
{
|
||||
public IActionResult Default() => this.Ok();
|
||||
|
||||
public IActionResult ActionWithParameter(int id) => this.Ok();
|
||||
|
||||
public IActionResult ActionWithStringParameter(string id, int num) => this.Ok();
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
@page
|
||||
Hello, OpenTelemetry!
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@page
|
||||
@{
|
||||
throw new Exception("Oops.");
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
// <copyright file="RouteInfo.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
#if NET8_0_OR_GREATER
|
||||
using Microsoft.AspNetCore.Http.Metadata;
|
||||
#endif
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace RouteTests.TestApplication;
|
||||
|
||||
public class RouteInfo
|
||||
{
|
||||
public static RouteInfo Current { get; set; } = new();
|
||||
|
||||
public string? HttpMethod { get; set; }
|
||||
|
||||
public string? Path { get; set; }
|
||||
|
||||
[JsonPropertyName("RoutePattern.RawText")]
|
||||
public string? RawText { get; set; }
|
||||
|
||||
[JsonPropertyName("IRouteDiagnosticsMetadata.Route")]
|
||||
public string? RouteDiagnosticMetadata { get; set; }
|
||||
|
||||
[JsonPropertyName("HttpContext.GetRouteData()")]
|
||||
public IDictionary<string, string?>? RouteData { get; set; }
|
||||
|
||||
public ActionDescriptorInfo? ActionDescriptor { get; set; }
|
||||
|
||||
public void SetValues(HttpContext context)
|
||||
{
|
||||
this.HttpMethod = context.Request.Method;
|
||||
this.Path = $"{context.Request.Path}{context.Request.QueryString}";
|
||||
var endpoint = context.GetEndpoint();
|
||||
this.RawText = (endpoint as RouteEndpoint)?.RoutePattern.RawText;
|
||||
#if NET8_0_OR_GREATER
|
||||
this.RouteDiagnosticMetadata = endpoint?.Metadata.GetMetadata<IRouteDiagnosticsMetadata>()?.Route;
|
||||
#endif
|
||||
this.RouteData = new Dictionary<string, string?>();
|
||||
foreach (var value in context.GetRouteData().Values)
|
||||
{
|
||||
this.RouteData[value.Key] = value.Value?.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValues(ActionDescriptor actionDescriptor)
|
||||
{
|
||||
if (this.ActionDescriptor == null)
|
||||
{
|
||||
this.ActionDescriptor = new ActionDescriptorInfo(actionDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public class ActionDescriptorInfo
|
||||
{
|
||||
public ActionDescriptorInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public ActionDescriptorInfo(ActionDescriptor actionDescriptor)
|
||||
{
|
||||
this.AttributeRouteInfo = actionDescriptor.AttributeRouteInfo?.Template;
|
||||
|
||||
this.ActionParameters = new List<string>();
|
||||
foreach (var item in actionDescriptor.Parameters)
|
||||
{
|
||||
this.ActionParameters.Add(item.Name);
|
||||
}
|
||||
|
||||
if (actionDescriptor is PageActionDescriptor pad)
|
||||
{
|
||||
this.PageActionDescriptorSummary = new PageActionDescriptorInfo(pad.RelativePath, pad.ViewEnginePath);
|
||||
}
|
||||
|
||||
if (actionDescriptor is ControllerActionDescriptor cad)
|
||||
{
|
||||
this.ControllerActionDescriptorSummary = new ControllerActionDescriptorInfo(cad.ControllerName, cad.ActionName);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("AttributeRouteInfo.Template")]
|
||||
public string? AttributeRouteInfo { get; set; }
|
||||
|
||||
[JsonPropertyName("Parameters")]
|
||||
public IList<string>? ActionParameters { get; set; }
|
||||
|
||||
[JsonPropertyName("ControllerActionDescriptor")]
|
||||
public ControllerActionDescriptorInfo? ControllerActionDescriptorSummary { get; set; }
|
||||
|
||||
[JsonPropertyName("PageActionDescriptor")]
|
||||
public PageActionDescriptorInfo? PageActionDescriptorSummary { get; set; }
|
||||
}
|
||||
|
||||
public class ControllerActionDescriptorInfo
|
||||
{
|
||||
public ControllerActionDescriptorInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public ControllerActionDescriptorInfo(string controllerName, string actionName)
|
||||
{
|
||||
this.ControllerActionDescriptorControllerName = controllerName;
|
||||
this.ControllerActionDescriptorActionName = actionName;
|
||||
}
|
||||
|
||||
[JsonPropertyName("ControllerName")]
|
||||
public string ControllerActionDescriptorControllerName { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("ActionName")]
|
||||
public string ControllerActionDescriptorActionName { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class PageActionDescriptorInfo
|
||||
{
|
||||
public PageActionDescriptorInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public PageActionDescriptorInfo(string relativePath, string viewEnginePath)
|
||||
{
|
||||
this.PageActionDescriptorRelativePath = relativePath;
|
||||
this.PageActionDescriptorViewEnginePath = viewEnginePath;
|
||||
}
|
||||
|
||||
[JsonPropertyName("RelativePath")]
|
||||
public string PageActionDescriptorRelativePath { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("ViewEnginePath")]
|
||||
public string PageActionDescriptorViewEnginePath { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// <copyright file="RouteInfoDiagnosticObserver.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Diagnostics;
|
||||
|
||||
namespace RouteTests.TestApplication;
|
||||
|
||||
/// <summary>
|
||||
/// This observer captures all the available route information for a request.
|
||||
/// This route information is used for generating a README file for analyzing
|
||||
/// what information is available in different scenarios.
|
||||
/// </summary>
|
||||
internal sealed class RouteInfoDiagnosticObserver : IDisposable, IObserver<DiagnosticListener>, IObserver<KeyValuePair<string, object?>>
|
||||
{
|
||||
internal const string OnStartEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start";
|
||||
internal const string OnStopEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop";
|
||||
internal const string OnMvcBeforeActionEvent = "Microsoft.AspNetCore.Mvc.BeforeAction";
|
||||
|
||||
private readonly List<IDisposable> listenerSubscriptions = new();
|
||||
private IDisposable? allSourcesSubscription;
|
||||
private long disposed;
|
||||
|
||||
public RouteInfoDiagnosticObserver()
|
||||
{
|
||||
this.allSourcesSubscription = DiagnosticListener.AllListeners.Subscribe(this);
|
||||
}
|
||||
|
||||
public void OnNext(DiagnosticListener value)
|
||||
{
|
||||
if (value.Name == "Microsoft.AspNetCore")
|
||||
{
|
||||
var subscription = value.Subscribe(this);
|
||||
|
||||
lock (this.listenerSubscriptions)
|
||||
{
|
||||
this.listenerSubscriptions.Add(subscription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNext(KeyValuePair<string, object?> value)
|
||||
{
|
||||
HttpContext? context;
|
||||
BeforeActionEventData? actionMethodEventData;
|
||||
RouteInfo? info;
|
||||
|
||||
switch (value.Key)
|
||||
{
|
||||
case OnStartEvent:
|
||||
context = value.Value as HttpContext;
|
||||
Debug.Assert(context != null, "HttpContext was null");
|
||||
info = new RouteInfo();
|
||||
info.SetValues(context);
|
||||
RouteInfo.Current = info;
|
||||
break;
|
||||
case OnMvcBeforeActionEvent:
|
||||
actionMethodEventData = value.Value as BeforeActionEventData;
|
||||
Debug.Assert(actionMethodEventData != null, $"expected {nameof(BeforeActionEventData)}");
|
||||
RouteInfo.Current.SetValues(actionMethodEventData.HttpContext);
|
||||
RouteInfo.Current.SetValues(actionMethodEventData.ActionDescriptor);
|
||||
break;
|
||||
case OnStopEvent:
|
||||
context = value.Value as HttpContext;
|
||||
Debug.Assert(context != null, "HttpContext was null");
|
||||
RouteInfo.Current.SetValues(context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (this.listenerSubscriptions)
|
||||
{
|
||||
foreach (var listenerSubscription in this.listenerSubscriptions)
|
||||
{
|
||||
listenerSubscription?.Dispose();
|
||||
}
|
||||
|
||||
this.listenerSubscriptions.Clear();
|
||||
}
|
||||
|
||||
this.allSourcesSubscription?.Dispose();
|
||||
this.allSourcesSubscription = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
// <copyright file="TestApplicationFactory.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace RouteTests.TestApplication;
|
||||
|
||||
public enum TestApplicationScenario
|
||||
{
|
||||
/// <summary>
|
||||
/// An application that uses conventional routing.
|
||||
/// </summary>
|
||||
ConventionalRouting,
|
||||
|
||||
/// <summary>
|
||||
/// An application that uses attribute routing.
|
||||
/// </summary>
|
||||
AttributeRouting,
|
||||
|
||||
/// <summary>
|
||||
/// A Minimal API application.
|
||||
/// </summary>
|
||||
MinimalApi,
|
||||
|
||||
/// <summary>
|
||||
/// An Razor Pages application.
|
||||
/// </summary>
|
||||
RazorPages,
|
||||
}
|
||||
|
||||
internal class TestApplicationFactory
|
||||
{
|
||||
private static readonly string AspNetCoreTestsPath = new FileInfo(typeof(RoutingTests)!.Assembly!.Location)!.Directory!.Parent!.Parent!.Parent!.FullName;
|
||||
private static readonly string ContentRootPath = Path.Combine(AspNetCoreTestsPath, "RouteTests", "TestApplication");
|
||||
|
||||
public static WebApplication? CreateApplication(TestApplicationScenario config)
|
||||
{
|
||||
Debug.Assert(Directory.Exists(ContentRootPath), $"Cannot find ContentRootPath: {ContentRootPath}");
|
||||
switch (config)
|
||||
{
|
||||
case TestApplicationScenario.ConventionalRouting:
|
||||
return CreateConventionalRoutingApplication();
|
||||
case TestApplicationScenario.AttributeRouting:
|
||||
return CreateAttributeRoutingApplication();
|
||||
case TestApplicationScenario.MinimalApi:
|
||||
return CreateMinimalApiApplication();
|
||||
case TestApplicationScenario.RazorPages:
|
||||
return CreateRazorPagesApplication();
|
||||
default:
|
||||
throw new ArgumentException($"Invalid {nameof(TestApplicationScenario)}");
|
||||
}
|
||||
}
|
||||
|
||||
private static WebApplication CreateConventionalRoutingApplication()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions { ContentRootPath = ContentRootPath });
|
||||
builder.Logging.ClearProviders();
|
||||
|
||||
builder.Services
|
||||
.AddControllersWithViews()
|
||||
.AddApplicationPart(typeof(RoutingTests).Assembly);
|
||||
|
||||
var app = builder.Build();
|
||||
app.Urls.Clear();
|
||||
app.Urls.Add("http://[::1]:0");
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
|
||||
app.MapAreaControllerRoute(
|
||||
name: "AnotherArea",
|
||||
areaName: "AnotherArea",
|
||||
pattern: "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}");
|
||||
|
||||
app.MapControllerRoute(
|
||||
name: "MyArea",
|
||||
pattern: "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}");
|
||||
|
||||
app.MapControllerRoute(
|
||||
name: "FixedRouteWithConstraints",
|
||||
pattern: "SomePath/{id}/{num:int}",
|
||||
defaults: new { controller = "ConventionalRoute", action = "ActionWithStringParameter" });
|
||||
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=ConventionalRoute}/{action=Default}/{id?}");
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static WebApplication CreateAttributeRoutingApplication()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.Logging.ClearProviders();
|
||||
|
||||
builder.Services
|
||||
.AddControllers()
|
||||
.AddApplicationPart(typeof(RoutingTests).Assembly);
|
||||
|
||||
var app = builder.Build();
|
||||
app.Urls.Clear();
|
||||
app.Urls.Add("http://[::1]:0");
|
||||
app.MapControllers();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static WebApplication CreateMinimalApiApplication()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.Logging.ClearProviders();
|
||||
|
||||
var app = builder.Build();
|
||||
app.Urls.Clear();
|
||||
app.Urls.Add("http://[::1]:0");
|
||||
|
||||
app.MapGet("/MinimalApi", () => Results.Ok());
|
||||
app.MapGet("/MinimalApi/{id}", (int id) => Results.Ok());
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
var api = app.MapGroup("/MinimalApiUsingMapGroup");
|
||||
api.MapGet("/", () => Results.Ok());
|
||||
api.MapGet("/{id}", (int id) => Results.Ok());
|
||||
#endif
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
private static WebApplication CreateRazorPagesApplication()
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions { ContentRootPath = ContentRootPath });
|
||||
builder.Logging.ClearProviders();
|
||||
|
||||
builder.Services
|
||||
.AddRazorPages()
|
||||
.AddRazorRuntimeCompilation(options =>
|
||||
{
|
||||
options.FileProviders.Add(new PhysicalFileProvider(ContentRootPath));
|
||||
})
|
||||
.AddApplicationPart(typeof(RoutingTests).Assembly);
|
||||
|
||||
var app = builder.Build();
|
||||
app.Urls.Clear();
|
||||
app.Urls.Add("http://[::1]:0");
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.MapRazorPages();
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
// for details on configuring this project to bundle and minify static web assets.
|
||||
|
||||
// Write your JavaScript code.
|
||||
Loading…
Reference in New Issue