Run request service constraint test with dispatching (#8112)
This commit is contained in:
parent
ec8976ffaf
commit
36d90c9bc2
|
|
@ -0,0 +1,13 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
public class RequestServicesDispatchingTest : RequestServicesTestBase<BasicWebSite.StartupWithDispatching>
|
||||
{
|
||||
public RequestServicesDispatchingTest(MvcTestFixture<BasicWebSite.StartupWithDispatching> fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +1,13 @@
|
|||
// 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;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
// Each of these tests makes two requests, because we want each test to verify that the data is
|
||||
// PER-REQUEST and does not linger around to impact the next request.
|
||||
public class RequestServicesTest : IClassFixture<MvcTestFixture<BasicWebSite.Startup>>
|
||||
public class RequestServicesTest : RequestServicesTestBase<BasicWebSite.Startup>
|
||||
{
|
||||
public RequestServicesTest(MvcTestFixture<BasicWebSite.Startup> fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
Client = fixture.CreateDefaultClient();
|
||||
}
|
||||
|
||||
public HttpClient Client { get; }
|
||||
|
||||
[Theory]
|
||||
[InlineData("http://localhost/RequestScopedService/FromFilter")]
|
||||
[InlineData("http://localhost/RequestScopedService/FromView")]
|
||||
[InlineData("http://localhost/RequestScopedService/FromViewComponent")]
|
||||
[InlineData("http://localhost/RequestScopedService/FromActionArgument")]
|
||||
public async Task RequestServices(string url)
|
||||
{
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
// Arrange
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.TryAddWithoutValidation("RequestId", requestId);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
response.EnsureSuccessStatusCode();
|
||||
var body = (await response.Content.ReadAsStringAsync()).Trim();
|
||||
Assert.Equal(requestId, body);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequestServices_TagHelper()
|
||||
{
|
||||
// Arrange
|
||||
var url = "http://localhost/RequestScopedService/FromTagHelper";
|
||||
|
||||
// Act & Assert
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.TryAddWithoutValidation("RequestId", requestId);
|
||||
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
var body = (await response.Content.ReadAsStringAsync()).Trim();
|
||||
|
||||
var expected = "<request-scoped>" + requestId + "</request-scoped>";
|
||||
Assert.Equal(expected, body);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequestServices_ActionConstraint()
|
||||
{
|
||||
// Arrange
|
||||
var url = "http://localhost/RequestScopedService/FromActionConstraint";
|
||||
|
||||
// Act & Assert
|
||||
var requestId1 = "b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5";
|
||||
var request1 = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request1.Headers.TryAddWithoutValidation("RequestId", requestId1);
|
||||
|
||||
var response1 = await Client.SendAsync(request1);
|
||||
|
||||
var body1 = (await response1.Content.ReadAsStringAsync()).Trim();
|
||||
Assert.Equal(requestId1, body1);
|
||||
|
||||
var requestId2 = Guid.NewGuid().ToString();
|
||||
var request2 = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request2.Headers.TryAddWithoutValidation("RequestId", requestId2);
|
||||
|
||||
var response2 = await Client.SendAsync(request2);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response2.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// 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;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
// Each of these tests makes two requests, because we want each test to verify that the data is
|
||||
// PER-REQUEST and does not linger around to impact the next request.
|
||||
public abstract class RequestServicesTestBase<TStartup> : IClassFixture<MvcTestFixture<TStartup>> where TStartup : class
|
||||
{
|
||||
protected RequestServicesTestBase(MvcTestFixture<TStartup> fixture)
|
||||
{
|
||||
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
|
||||
Client = factory.CreateDefaultClient();
|
||||
}
|
||||
|
||||
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
|
||||
builder.UseStartup<TStartup>();
|
||||
|
||||
public HttpClient Client { get; }
|
||||
|
||||
[Theory]
|
||||
[InlineData("http://localhost/RequestScopedService/FromFilter")]
|
||||
[InlineData("http://localhost/RequestScopedService/FromView")]
|
||||
[InlineData("http://localhost/RequestScopedService/FromViewComponent")]
|
||||
[InlineData("http://localhost/RequestScopedService/FromActionArgument")]
|
||||
public async Task RequestServices(string url)
|
||||
{
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
// Arrange
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.TryAddWithoutValidation("RequestId", requestId);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
response.EnsureSuccessStatusCode();
|
||||
var body = (await response.Content.ReadAsStringAsync()).Trim();
|
||||
Assert.Equal(requestId, body);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequestServices_TagHelper()
|
||||
{
|
||||
// Arrange
|
||||
var url = "http://localhost/RequestScopedService/FromTagHelper";
|
||||
|
||||
// Act & Assert
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.TryAddWithoutValidation("RequestId", requestId);
|
||||
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
var body = (await response.Content.ReadAsStringAsync()).Trim();
|
||||
|
||||
var expected = "<request-scoped>" + requestId + "</request-scoped>";
|
||||
Assert.Equal(expected, body);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RequestServices_Constraint()
|
||||
{
|
||||
// Arrange
|
||||
var url = "http://localhost/RequestScopedService/FromConstraint";
|
||||
|
||||
// Act & Assert
|
||||
var requestId1 = "b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5";
|
||||
var request1 = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request1.Headers.TryAddWithoutValidation("RequestId", requestId1);
|
||||
|
||||
var response1 = await Client.SendAsync(request1);
|
||||
|
||||
var body1 = (await response1.Content.ReadAsStringAsync()).Trim();
|
||||
Assert.Equal(requestId1, body1);
|
||||
|
||||
var requestId2 = Guid.NewGuid().ToString();
|
||||
var request2 = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request2.Headers.TryAddWithoutValidation("RequestId", requestId2);
|
||||
|
||||
var response2 = await Client.SendAsync(request2);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response2.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,8 +10,8 @@ namespace BasicWebSite
|
|||
{
|
||||
// This only matches a specific requestId value
|
||||
[HttpGet]
|
||||
[RequestScopedActionConstraint("b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5")]
|
||||
public string FromActionConstraint()
|
||||
[RequestScopedConstraint("b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5")]
|
||||
public string FromConstraint()
|
||||
{
|
||||
return "b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Routing.EndpointConstraints;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BasicWebSite
|
||||
{
|
||||
// Only matches when the requestId is the same as the one passed in the constructor.
|
||||
public class RequestScopedActionConstraintAttribute : Attribute, IActionConstraintFactory
|
||||
public class RequestScopedConstraintAttribute : Attribute, IActionConstraintFactory, IEndpointConstraintFactory
|
||||
{
|
||||
private readonly string _requestId;
|
||||
private readonly Func<Type, object, ObjectFactory> CreateFactory =
|
||||
|
|
@ -19,18 +20,28 @@ namespace BasicWebSite
|
|||
|
||||
public bool IsReusable => false;
|
||||
|
||||
public RequestScopedActionConstraintAttribute(string requestId)
|
||||
public RequestScopedConstraintAttribute(string requestId)
|
||||
{
|
||||
_requestId = requestId;
|
||||
}
|
||||
|
||||
public IActionConstraint CreateInstance(IServiceProvider services)
|
||||
IActionConstraint IActionConstraintFactory.CreateInstance(IServiceProvider services)
|
||||
{
|
||||
var constraintType = typeof(Constraint);
|
||||
return (Constraint)ActivatorUtilities.CreateInstance(services, typeof(Constraint),new[] { _requestId });
|
||||
return CreateInstanceCore(services);
|
||||
}
|
||||
|
||||
private class Constraint : IActionConstraint
|
||||
IEndpointConstraint IEndpointConstraintFactory.CreateInstance(IServiceProvider services)
|
||||
{
|
||||
return CreateInstanceCore(services);
|
||||
}
|
||||
|
||||
private Constraint CreateInstanceCore(IServiceProvider services)
|
||||
{
|
||||
var constraintType = typeof(Constraint);
|
||||
return (Constraint)ActivatorUtilities.CreateInstance(services, typeof(Constraint), new[] { _requestId });
|
||||
}
|
||||
|
||||
private class Constraint : IActionConstraint, IEndpointConstraint
|
||||
{
|
||||
private readonly RequestIdService _requestIdService;
|
||||
private readonly string _requestId;
|
||||
|
|
@ -43,7 +54,17 @@ namespace BasicWebSite
|
|||
|
||||
public int Order { get; private set; }
|
||||
|
||||
public bool Accept(ActionConstraintContext context)
|
||||
bool IActionConstraint.Accept(ActionConstraintContext context)
|
||||
{
|
||||
return AcceptCore();
|
||||
}
|
||||
|
||||
bool IEndpointConstraint.Accept(EndpointConstraintContext context)
|
||||
{
|
||||
return AcceptCore();
|
||||
}
|
||||
|
||||
private bool AcceptCore()
|
||||
{
|
||||
return _requestId == _requestIdService.RequestId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,16 @@ namespace BasicWebSite
|
|||
.AddXmlDataContractSerializerFormatters();
|
||||
|
||||
services.ConfigureBaseWebSiteAuthPolicies();
|
||||
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<RequestIdService>();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// Initializes the RequestId service for each request
|
||||
app.UseMiddleware<RequestIdMiddleware>();
|
||||
|
||||
app.UseDispatcher();
|
||||
|
||||
app.UseMvcWithEndpoint(routes =>
|
||||
|
|
|
|||
Loading…
Reference in New Issue