parent
f49cbd1b25
commit
2f8951e244
|
|
@ -7,11 +7,9 @@ using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Dispatcher;
|
using Microsoft.AspNetCore.Dispatcher;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
|
||||||
using Microsoft.AspNetCore.Routing.Dispatcher;
|
using Microsoft.AspNetCore.Routing.Dispatcher;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace DispatcherSample
|
namespace DispatcherSample
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Dispatcher;
|
|
||||||
|
|
||||||
namespace DispatcherSample
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
{
|
{
|
||||||
public class HttpMethodEndpointSelector : EndpointSelector
|
public class HttpMethodEndpointSelector : EndpointSelector
|
||||||
{
|
{
|
||||||
|
|
@ -19,19 +18,19 @@ namespace DispatcherSample
|
||||||
|
|
||||||
var snapshot = context.CreateSnapshot();
|
var snapshot = context.CreateSnapshot();
|
||||||
|
|
||||||
var fallback = new List<Endpoint>();
|
var fallbackEndpoints = new List<Endpoint>();
|
||||||
for (var i = context.Endpoints.Count - 1; i >= 0; i--)
|
for (var i = context.Endpoints.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var endpoint = context.Endpoints[i] as ITemplateEndpoint;
|
var endpoint = context.Endpoints[i] as ITemplateEndpoint;
|
||||||
if (endpoint == null || endpoint.HttpMethod == null)
|
if (endpoint == null || endpoint.HttpMethod == null)
|
||||||
{
|
{
|
||||||
// No metadata.
|
// No metadata.
|
||||||
fallback.Add(context.Endpoints[i]);
|
fallbackEndpoints.Add(context.Endpoints[i]);
|
||||||
context.Endpoints.RemoveAt(i);
|
context.Endpoints.RemoveAt(i);
|
||||||
}
|
}
|
||||||
else if (string.Equals(endpoint.HttpMethod, context.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(endpoint.HttpMethod, context.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// This one matches.
|
// The request method matches the endpoint's HTTP method.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -50,9 +49,9 @@ namespace DispatcherSample
|
||||||
context.RestoreSnapshot(snapshot);
|
context.RestoreSnapshot(snapshot);
|
||||||
context.Endpoints.Clear();
|
context.Endpoints.Clear();
|
||||||
|
|
||||||
for (var i = 0; i < fallback.Count; i++)
|
for (var i = 0; i < fallbackEndpoints.Count; i++)
|
||||||
{
|
{
|
||||||
context.Endpoints.Add(fallback[i]);
|
context.Endpoints.Add(fallbackEndpoints[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.InvokeNextAsync();
|
await context.InvokeNextAsync();
|
||||||
|
|
@ -167,7 +167,7 @@ namespace Microsoft.AspNetCore.Routing.Dispatcher
|
||||||
trees.Add(new UrlMatchingTree(trees.Count));
|
trees.Add(new UrlMatchingTree(trees.Count));
|
||||||
}
|
}
|
||||||
|
|
||||||
var tree = trees[i];
|
var tree = trees[entry.Order];
|
||||||
|
|
||||||
TreeRouteBuilder.AddEntryToTree(tree, entry);
|
TreeRouteBuilder.AddEntryToTree(tree, entry);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,15 +62,31 @@ namespace Microsoft.AspNetCore.Dispatcher.FunctionalTest
|
||||||
{
|
{
|
||||||
Endpoints =
|
Endpoints =
|
||||||
{
|
{
|
||||||
new TemplateEndpoint("api/products", Products_Get),
|
new TemplateEndpoint("api/products", Products_Fallback),
|
||||||
|
new TemplateEndpoint("api/products", new { controller = "Products", action = "Get", }, "GET", Products_Get),
|
||||||
|
new TemplateEndpoint("api/products/{id}", new { controller = "Products", action = "Get", }, "GET", Products_GetWithId),
|
||||||
|
new TemplateEndpoint("api/products", new { controller = "Products", action = "Post", }, "POST", Products_Post),
|
||||||
|
new TemplateEndpoint("api/products/{id}", new { controller = "Products", action = "Put", }, "PUT", Products_Put),
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
|
||||||
|
Selectors =
|
||||||
|
{
|
||||||
|
new HttpMethodEndpointSelector(),
|
||||||
|
}
|
||||||
|
});
|
||||||
options.HandlerFactories.Add(endpoint => (endpoint as TemplateEndpoint)?.HandlerFactory);
|
options.HandlerFactories.Add(endpoint => (endpoint as TemplateEndpoint)?.HandlerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task Products_Fallback(HttpContext httpContext) => httpContext.Response.WriteAsync("Hello, Products_Fallback");
|
||||||
|
|
||||||
private Task Products_Get(HttpContext httpContext) => httpContext.Response.WriteAsync("Hello, Products_Get");
|
private Task Products_Get(HttpContext httpContext) => httpContext.Response.WriteAsync("Hello, Products_Get");
|
||||||
|
|
||||||
|
private Task Products_GetWithId(HttpContext httpContext) => httpContext.Response.WriteAsync("Hello, Products_GetWithId");
|
||||||
|
|
||||||
|
private Task Products_Post(HttpContext httpContext) => httpContext.Response.WriteAsync("Hello, Products_Post");
|
||||||
|
|
||||||
|
private Task Products_Put(HttpContext httpContext) => httpContext.Response.WriteAsync("Hello, Products_Put");
|
||||||
|
|
||||||
private class CorsPolicyMetadata
|
private class CorsPolicyMetadata
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,73 @@ namespace Microsoft.AspNetCore.Dispatcher.FunctionalTest
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
Assert.Equal("Hello, Products_Get", await response.Content.ReadAsStringAsync());
|
Assert.Equal("Hello, Products_Get", await response.Content.ReadAsStringAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiApp_RoutesTo_EndpointWithMatchingHttpMethod()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "/api/products");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("Hello, Products_Post", await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiApp_RoutesTo_EndpointWithMatchingHttpMethod_AndMatchingRoute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "/api/products/3");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("Hello, Products_GetWithId", await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiApp_RoutesTo_EndpointWithMatchingHttpMethod_DoesNotMatchExpectedRoute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Put, "/api/services/2");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiApp_NoEndpointWithMatchingHttpMethod_FallbackEndpointSelected()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Delete, "/api/products");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("Hello, Products_Fallback", await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiApp_NoEndpointWithMatchingHttpMethod_NoFallbackEndpointMatched()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Delete, "/api/products/4");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Dispatcher.Abstractions.Test
|
|
||||||
{
|
|
||||||
public class DispatcherTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Test()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
public class HttpMethodEndpointSelectorTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("get")]
|
||||||
|
[InlineData("Get")]
|
||||||
|
[InlineData("GET")]
|
||||||
|
public async Task RequestMethod_MatchesEndpointMethod_IgnoresCase(string httpMethod)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var endpoints = new List<Endpoint>()
|
||||||
|
{
|
||||||
|
new TemplateEndpoint("{controller=Home}/{action=Index}/{id?}", new { controller = "Products", action = "Get", }, "GET", Products_Get, "Products:Get()"),
|
||||||
|
new TemplateEndpoint("{controller=Home}/{action=Index}/{id?}", new { controller = "Products", action = "Create", }, "POST", Products_Post, "Products:Post()"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var (context, selector) = CreateContextAndSelector(httpMethod, endpoints);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await selector.SelectAsync(context);
|
||||||
|
var templateEndpoints = context.Endpoints.Cast<TemplateEndpoint>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
templateEndpoints,
|
||||||
|
endpoint => Assert.Equal(httpMethod.ToUpperInvariant(), endpoint.HttpMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RequestMethod_DoesNotMatch_AnyEndpointMethod()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var endpoints = new List<Endpoint>()
|
||||||
|
{
|
||||||
|
new TemplateEndpoint("{controller=Home}/{action=Index}/{id?}", new { controller = "Products", action = "Get", }, "GET", Products_Get, "Products:Get()"),
|
||||||
|
new TemplateEndpoint("{controller=Home}/{action=Index}/{id?}", new { controller = "Products", action = "Create", }, "POST", Products_Post, "Products:Post()"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var (context, selector) = CreateContextAndSelector("PUT", endpoints);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await selector.SelectAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(0, context.Endpoints.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("PUT")]
|
||||||
|
[InlineData(null)]
|
||||||
|
public async Task RequestMethod_NotSpecifiedOrNotFound_ReturnsFallbackEndpointMethod(string httpMethod)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var endpoints = new List<Endpoint>()
|
||||||
|
{
|
||||||
|
new TemplateEndpoint("{controller=Home}/{action=Index}/{id?}", new { controller = "Products", action = "Get", }, "GET", Products_Get, "Products:Get()"),
|
||||||
|
new TemplateEndpoint("{controller=Home}/{action=Index}/{id?}", new { controller = "Products", action = "Create", }, "POST", Products_Post, "Products:Post()"),
|
||||||
|
new TemplateEndpoint("{controller=Home}/{action=Index}/{id?}", new { controller = "Products", action = "Get", }, Products_Get),
|
||||||
|
};
|
||||||
|
|
||||||
|
var (context, selector) = CreateContextAndSelector(httpMethod, endpoints);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await selector.SelectAsync(context);
|
||||||
|
var templateEndpoints = context.Endpoints.Cast<TemplateEndpoint>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
templateEndpoints,
|
||||||
|
endpoint => Assert.Null(endpoint.HttpMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
private (EndpointSelectorContext, EndpointSelector) CreateContextAndSelector(string httpMethod, List<Endpoint> endpoints)
|
||||||
|
{
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.Request.Method = httpMethod;
|
||||||
|
var selector = new HttpMethodEndpointSelector();
|
||||||
|
var selectors = new List<EndpointSelector>()
|
||||||
|
{
|
||||||
|
selector
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectorContext = new EndpointSelectorContext(httpContext, endpoints, selectors);
|
||||||
|
return (selectorContext, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task Products_Get(HttpContext httpContext) => httpContext.Response.WriteAsync("Hello, Products_Get");
|
||||||
|
|
||||||
|
private Task Products_Post(HttpContext httpContext) => httpContext.Response.WriteAsync("Hello, Products_Post");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,4 +9,8 @@
|
||||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Dispatcher\Microsoft.AspNetCore.Dispatcher.csproj" />
|
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Dispatcher\Microsoft.AspNetCore.Dispatcher.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Http" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue