Skip to content

Commit 3cc4dcb

Browse files
authored
[Blazor] Add HTTP HEAD method support to RazorComponentEndpoints (#64613)
* Adds HEAD support for Blazor SSR.
1 parent 6b8e991 commit 3cc4dcb

File tree

4 files changed

+63
-1
lines changed

4 files changed

+63
-1
lines changed

src/Components/Endpoints/src/Builder/RazorComponentEndpointFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Components.Endpoints;
1515

1616
internal class RazorComponentEndpointFactory
1717
{
18-
private static readonly HttpMethodMetadata HttpMethodsMetadata = new([HttpMethods.Get, HttpMethods.Post]);
18+
private static readonly HttpMethodMetadata HttpMethodsMetadata = new([HttpMethods.Get, HttpMethods.Head, HttpMethods.Post]);
1919

2020
#pragma warning disable CA1822 // It's a singleton
2121
internal void AddEndpoints(

src/Components/Endpoints/test/RazorComponentEndpointFactoryTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public void AddEndpoints_CreatesEndpointWithExpectedMetadata()
4747
var methods = Assert.Single(endpoint.Metadata.GetOrderedMetadata<HttpMethodMetadata>());
4848
Assert.Collection(methods.HttpMethods,
4949
method => Assert.Equal("GET", method),
50+
method => Assert.Equal("HEAD", method),
5051
method => Assert.Equal("POST", method)
5152
);
5253
}

src/Components/Endpoints/test/RazorComponentEndpointInvokerTest.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using Microsoft.AspNetCore.Components.Authorization;
5+
using Microsoft.AspNetCore.Components.Endpoints.Tests.TestComponents;
56
using Microsoft.AspNetCore.Hosting;
67
using Microsoft.AspNetCore.Http;
78
using Microsoft.AspNetCore.Routing;
@@ -51,6 +52,49 @@ public async Task Invoker_RejectsPostRequestsWithNonFormDataContentTypesAsync()
5152
Assert.Equal(StatusCodes.Status400BadRequest, context.Response.StatusCode);
5253
}
5354

55+
[Fact]
56+
public async Task Invoker_HandlesHeadRequestAsync()
57+
{
58+
// Arrange
59+
var services = new ServiceCollection().AddRazorComponents()
60+
.Services.AddAntiforgery()
61+
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
62+
.AddSingleton<IWebHostEnvironment>(new TestWebHostEnvironment())
63+
.BuildServiceProvider();
64+
65+
var invoker = new RazorComponentEndpointInvoker(
66+
new EndpointHtmlRenderer(
67+
services,
68+
NullLoggerFactory.Instance),
69+
NullLogger<RazorComponentEndpointInvoker>.Instance);
70+
71+
var context = new DefaultHttpContext();
72+
context.SetEndpoint(new RouteEndpoint(
73+
ctx => Task.CompletedTask,
74+
RoutePatternFactory.Parse("/"),
75+
0,
76+
new EndpointMetadataCollection(
77+
new ComponentTypeMetadata(typeof(SimpleComponent)),
78+
new RootComponentMetadata(typeof(SimpleComponent)),
79+
new ConfiguredRenderModesMetadata(Array.Empty<IComponentRenderMode>())),
80+
"test"));
81+
context.Request.Method = "HEAD";
82+
context.Request.Scheme = "https";
83+
context.Request.Host = new HostString("localhost");
84+
context.Request.Path = "/";
85+
context.Response.Body = new MemoryStream();
86+
context.RequestServices = services;
87+
88+
// Act
89+
await invoker.Render(context);
90+
91+
// Assert
92+
// HEAD requests should execute the full request like GET, returning 200 OK with headers.
93+
// The HTTP server (Kestrel) handles suppressing the response body for HEAD requests.
94+
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
95+
Assert.Equal("text/html; charset=utf-8", context.Response.ContentType);
96+
}
97+
5498
private class TestWebHostEnvironment : IWebHostEnvironment
5599
{
56100
public string WebRootPath { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

src/Components/test/E2ETest/Tests/GlobalInteractivityTest.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,21 @@ public void StatusCodePagesWithReExecution()
195195
}
196196
private void AssertReExecutedPageRendered() =>
197197
Browser.Equal("Welcome On Page Re-executed After Not Found Event", () => Browser.Exists(By.Id("test-info")).Text);
198+
199+
[Fact]
200+
public async Task HeadRequestReturnsSuccessWithNoBody()
201+
{
202+
// Arrange
203+
using var client = new HttpClient() { BaseAddress = _serverFixture.RootUri };
204+
var request = new HttpRequestMessage(HttpMethod.Head, "/subdir/globally-interactive");
205+
206+
// Act
207+
var response = await client.SendAsync(request);
208+
209+
// Assert
210+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
211+
Assert.Equal("text/html; charset=utf-8", response.Content.Headers.ContentType?.ToString());
212+
var body = await response.Content.ReadAsStringAsync();
213+
Assert.Empty(body);
214+
}
198215
}

0 commit comments

Comments
 (0)