Run request service constraint test with dispatching (#8112)

This commit is contained in:
James Newton-King 2018-07-19 16:43:51 +12:00 committed by GitHub
parent ec8976ffaf
commit 36d90c9bc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 91 deletions

View File

@ -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)
{
}
}
}

View File

@ -1,93 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{ {
// Each of these tests makes two requests, because we want each test to verify that the data is public class RequestServicesTest : RequestServicesTestBase<BasicWebSite.Startup>
// PER-REQUEST and does not linger around to impact the next request.
public class RequestServicesTest : IClassFixture<MvcTestFixture<BasicWebSite.Startup>>
{ {
public RequestServicesTest(MvcTestFixture<BasicWebSite.Startup> fixture) 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);
} }
} }
} }

View File

@ -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);
}
}
}

View File

@ -10,8 +10,8 @@ namespace BasicWebSite
{ {
// This only matches a specific requestId value // This only matches a specific requestId value
[HttpGet] [HttpGet]
[RequestScopedActionConstraint("b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5")] [RequestScopedConstraint("b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5")]
public string FromActionConstraint() public string FromConstraint()
{ {
return "b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5"; return "b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5";
} }

View File

@ -4,12 +4,13 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Routing.EndpointConstraints;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace BasicWebSite namespace BasicWebSite
{ {
// Only matches when the requestId is the same as the one passed in the constructor. // 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 string _requestId;
private readonly Func<Type, object, ObjectFactory> CreateFactory = private readonly Func<Type, object, ObjectFactory> CreateFactory =
@ -19,18 +20,28 @@ namespace BasicWebSite
public bool IsReusable => false; public bool IsReusable => false;
public RequestScopedActionConstraintAttribute(string requestId) public RequestScopedConstraintAttribute(string requestId)
{ {
_requestId = requestId; _requestId = requestId;
} }
public IActionConstraint CreateInstance(IServiceProvider services) IActionConstraint IActionConstraintFactory.CreateInstance(IServiceProvider services)
{ {
var constraintType = typeof(Constraint); return CreateInstanceCore(services);
return (Constraint)ActivatorUtilities.CreateInstance(services, typeof(Constraint),new[] { _requestId });
} }
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 RequestIdService _requestIdService;
private readonly string _requestId; private readonly string _requestId;
@ -43,7 +54,17 @@ namespace BasicWebSite
public int Order { get; private set; } 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; return _requestId == _requestIdService.RequestId;
} }

View File

@ -19,10 +19,16 @@ namespace BasicWebSite
.AddXmlDataContractSerializerFormatters(); .AddXmlDataContractSerializerFormatters();
services.ConfigureBaseWebSiteAuthPolicies(); services.ConfigureBaseWebSiteAuthPolicies();
services.AddHttpContextAccessor();
services.AddScoped<RequestIdService>();
} }
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)
{ {
// Initializes the RequestId service for each request
app.UseMiddleware<RequestIdMiddleware>();
app.UseDispatcher(); app.UseDispatcher();
app.UseMvcWithEndpoint(routes => app.UseMvcWithEndpoint(routes =>