소스 검색

Caching common HttpResults types (#40965)

* Caching known results

* removing generated code

* Updating files

* Updating csproj

* Updating autogenerated code

* Removing non-lazy cache

* formatting fix

* small perf condition

* small perf

* Fixing formatting

* Range adjustment

* removing template debug flag

* Removing HTTP 100

* Adding unit test for t4 template

* Fixing ubuntu test

* Update src/Http/README.md

Co-authored-by: Günther Foidl <[email protected]>

* Update src/Http/Http.Results/test/ResultsCacheTests.cs

Co-authored-by: Brennan <[email protected]>

* PR Feedback

Co-authored-by: Günther Foidl <[email protected]>
Co-authored-by: Brennan <[email protected]>
Bruno Oliveira 3 년 전
부모
커밋
e9b2adb2a2

+ 1 - 0
eng/Dependencies.props

@@ -189,6 +189,7 @@ and are generated based on the last package release.
     <LatestPackageReference Include="MessagePackAnalyzer" />
     <LatestPackageReference Include="Microsoft.Data.SqlClient" />
     <LatestPackageReference Include="Mono.Cecil" />
+    <LatestPackageReference Include="Mono.TextTemplating" />
     <LatestPackageReference Include="Moq" />
     <LatestPackageReference Include="Newtonsoft.Json.Bson" />
     <LatestPackageReference Include="NSwag.ApiDescription.Client" />

+ 1 - 0
eng/Versions.props

@@ -257,6 +257,7 @@
     <MessagePackAnalyzerVersion>$(MessagePackVersion)</MessagePackAnalyzerVersion>
     <MoqVersion>4.10.0</MoqVersion>
     <MonoCecilVersion>0.11.2</MonoCecilVersion>
+    <MonoTextTemplatingVersion>2.2.1</MonoTextTemplatingVersion>
     <NewtonsoftJsonBsonVersion>1.0.2</NewtonsoftJsonBsonVersion>
     <NewtonsoftJsonVersion>13.0.1</NewtonsoftJsonVersion>
     <NSwagApiDescriptionClientVersion>13.0.4</NSwagApiDescriptionClientVersion>

+ 7 - 0
src/Http/Http.Results/Directory.Build.props

@@ -0,0 +1,7 @@
+<Project>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
+
+  <PropertyGroup>
+    <HttpResultsSourceRoot>$(MSBuildThisFileDirectory)\src</HttpResultsSourceRoot>
+  </PropertyGroup>
+</Project>

+ 12 - 0
src/Http/Http.Results/src/Microsoft.AspNetCore.Http.Results.csproj

@@ -28,4 +28,16 @@
     <InternalsVisibleTo Include="Microsoft.AspNetCore.Http.Results.Tests" />
   </ItemGroup>
 
+  <ItemGroup>
+    <!-- This is the T4 template service and is added by VS anytime you modify a T4 template. Required for .tt files. -->
+    <Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="ResultsCache.StatusCodes.tt">
+      <Generator>TextTemplatingFileGenerator</Generator>
+      <LastGenOutput>ResultsCache.StatusCodes.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+
 </Project>

+ 8 - 8
src/Http/Http.Results/src/Results.cs

@@ -495,7 +495,7 @@ public static class Results
     /// <param name="statusCode">The status code to set on the response.</param>
     /// <returns>The created <see cref="StatusCodeHttpResult"/> object for the response.</returns>
     public static IResult StatusCode(int statusCode)
-        => new StatusCodeHttpResult(statusCode);
+        => ResultsCache.StatusCode(statusCode);
 
     /// <summary>
     /// Produces a <see cref="StatusCodes.Status404NotFound"/> response.
@@ -503,14 +503,14 @@ public static class Results
     /// <param name="value">The value to be included in the HTTP response body.</param>
     /// <returns>The created <see cref="IResult"/> for the response.</returns>
     public static IResult NotFound(object? value = null)
-        => new NotFoundObjectHttpResult(value);
+        => value is null ? ResultsCache.NotFound : new NotFoundObjectHttpResult(value);
 
     /// <summary>
     /// Produces a <see cref="StatusCodes.Status401Unauthorized"/> response.
     /// </summary>
     /// <returns>The created <see cref="IResult"/> for the response.</returns>
     public static IResult Unauthorized()
-        => new UnauthorizedHttpResult();
+        => ResultsCache.Unauthorized;
 
     /// <summary>
     /// Produces a <see cref="StatusCodes.Status400BadRequest"/> response.
@@ -518,7 +518,7 @@ public static class Results
     /// <param name="error">An error object to be included in the HTTP response body.</param>
     /// <returns>The created <see cref="IResult"/> for the response.</returns>
     public static IResult BadRequest(object? error = null)
-        => new BadRequestObjectHttpResult(error);
+        => error is null ? ResultsCache.BadRequest : new BadRequestObjectHttpResult(error);
 
     /// <summary>
     /// Produces a <see cref="StatusCodes.Status409Conflict"/> response.
@@ -526,14 +526,14 @@ public static class Results
     /// <param name="error">An error object to be included in the HTTP response body.</param>
     /// <returns>The created <see cref="IResult"/> for the response.</returns>
     public static IResult Conflict(object? error = null)
-        => new ConflictObjectHttpResult(error);
+        => error is null ? ResultsCache.Conflict : new ConflictObjectHttpResult(error);
 
     /// <summary>
     /// Produces a <see cref="StatusCodes.Status204NoContent"/> response.
     /// </summary>
     /// <returns>The created <see cref="IResult"/> for the response.</returns>
     public static IResult NoContent()
-        => new NoContentHttpResult();
+        => ResultsCache.NoContent;
 
     /// <summary>
     /// Produces a <see cref="StatusCodes.Status200OK"/> response.
@@ -541,7 +541,7 @@ public static class Results
     /// <param name="value">The value to be included in the HTTP response body.</param>
     /// <returns>The created <see cref="IResult"/> for the response.</returns>
     public static IResult Ok(object? value = null)
-        => new OkObjectHttpResult(value);
+        => value is null ? ResultsCache.Ok : new OkObjectHttpResult(value);
 
     /// <summary>
     /// Produces a <see cref="StatusCodes.Status422UnprocessableEntity"/> response.
@@ -549,7 +549,7 @@ public static class Results
     /// <param name="error">An error object to be included in the HTTP response body.</param>
     /// <returns>The created <see cref="IResult"/> for the response.</returns>
     public static IResult UnprocessableEntity(object? error = null)
-        => new UnprocessableEntityObjectHttpResult(error);
+        => error is null ? ResultsCache.UnprocessableEntity : new UnprocessableEntityObjectHttpResult(error);
 
     /// <summary>
     /// Produces a <see cref="ProblemDetails"/> response.

+ 151 - 0
src/Http/Http.Results/src/ResultsCache.StatusCodes.cs

@@ -0,0 +1,151 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// <auto-generated />
+
+#nullable enable
+
+using System.CodeDom.Compiler;
+
+namespace Microsoft.AspNetCore.Http;
+
+[GeneratedCode("TextTemplatingFileGenerator", "")]
+internal partial class ResultsCache
+{
+    private static StatusCodeHttpResult? _status101SwitchingProtocols;
+    private static StatusCodeHttpResult? _status102Processing;
+    private static StatusCodeHttpResult? _status200OK;
+    private static StatusCodeHttpResult? _status201Created;
+    private static StatusCodeHttpResult? _status202Accepted;
+    private static StatusCodeHttpResult? _status203NonAuthoritative;
+    private static StatusCodeHttpResult? _status204NoContent;
+    private static StatusCodeHttpResult? _status205ResetContent;
+    private static StatusCodeHttpResult? _status206PartialContent;
+    private static StatusCodeHttpResult? _status207MultiStatus;
+    private static StatusCodeHttpResult? _status208AlreadyReported;
+    private static StatusCodeHttpResult? _status226IMUsed;
+    private static StatusCodeHttpResult? _status300MultipleChoices;
+    private static StatusCodeHttpResult? _status301MovedPermanently;
+    private static StatusCodeHttpResult? _status302Found;
+    private static StatusCodeHttpResult? _status303SeeOther;
+    private static StatusCodeHttpResult? _status304NotModified;
+    private static StatusCodeHttpResult? _status305UseProxy;
+    private static StatusCodeHttpResult? _status306SwitchProxy;
+    private static StatusCodeHttpResult? _status307TemporaryRedirect;
+    private static StatusCodeHttpResult? _status308PermanentRedirect;
+    private static StatusCodeHttpResult? _status400BadRequest;
+    private static StatusCodeHttpResult? _status401Unauthorized;
+    private static StatusCodeHttpResult? _status402PaymentRequired;
+    private static StatusCodeHttpResult? _status403Forbidden;
+    private static StatusCodeHttpResult? _status404NotFound;
+    private static StatusCodeHttpResult? _status405MethodNotAllowed;
+    private static StatusCodeHttpResult? _status406NotAcceptable;
+    private static StatusCodeHttpResult? _status407ProxyAuthenticationRequired;
+    private static StatusCodeHttpResult? _status408RequestTimeout;
+    private static StatusCodeHttpResult? _status409Conflict;
+    private static StatusCodeHttpResult? _status410Gone;
+    private static StatusCodeHttpResult? _status411LengthRequired;
+    private static StatusCodeHttpResult? _status412PreconditionFailed;
+    private static StatusCodeHttpResult? _status413RequestEntityTooLarge;
+    private static StatusCodeHttpResult? _status414RequestUriTooLong;
+    private static StatusCodeHttpResult? _status415UnsupportedMediaType;
+    private static StatusCodeHttpResult? _status416RequestedRangeNotSatisfiable;
+    private static StatusCodeHttpResult? _status417ExpectationFailed;
+    private static StatusCodeHttpResult? _status418ImATeapot;
+    private static StatusCodeHttpResult? _status419AuthenticationTimeout;
+    private static StatusCodeHttpResult? _status421MisdirectedRequest;
+    private static StatusCodeHttpResult? _status422UnprocessableEntity;
+    private static StatusCodeHttpResult? _status423Locked;
+    private static StatusCodeHttpResult? _status424FailedDependency;
+    private static StatusCodeHttpResult? _status426UpgradeRequired;
+    private static StatusCodeHttpResult? _status428PreconditionRequired;
+    private static StatusCodeHttpResult? _status429TooManyRequests;
+    private static StatusCodeHttpResult? _status431RequestHeaderFieldsTooLarge;
+    private static StatusCodeHttpResult? _status451UnavailableForLegalReasons;
+    private static StatusCodeHttpResult? _status500InternalServerError;
+    private static StatusCodeHttpResult? _status501NotImplemented;
+    private static StatusCodeHttpResult? _status502BadGateway;
+    private static StatusCodeHttpResult? _status503ServiceUnavailable;
+    private static StatusCodeHttpResult? _status504GatewayTimeout;
+    private static StatusCodeHttpResult? _status505HttpVersionNotsupported;
+    private static StatusCodeHttpResult? _status506VariantAlsoNegotiates;
+    private static StatusCodeHttpResult? _status507InsufficientStorage;
+    private static StatusCodeHttpResult? _status508LoopDetected;
+    private static StatusCodeHttpResult? _status510NotExtended;
+    private static StatusCodeHttpResult? _status511NetworkAuthenticationRequired;
+
+    internal static StatusCodeHttpResult StatusCode(int statusCode)
+    {
+        if (statusCode is (< 100) or (> 599))
+        {
+            // No HTTP status code assigned outside the 100..599 range
+            // so, it will not be available in the cache
+            return new StatusCodeHttpResult(statusCode);
+        }
+
+        return statusCode switch
+        {
+            StatusCodes.Status101SwitchingProtocols => _status101SwitchingProtocols ??= new(StatusCodes.Status101SwitchingProtocols),
+            StatusCodes.Status102Processing => _status102Processing ??= new(StatusCodes.Status102Processing),
+            StatusCodes.Status200OK => _status200OK ??= new(StatusCodes.Status200OK),
+            StatusCodes.Status201Created => _status201Created ??= new(StatusCodes.Status201Created),
+            StatusCodes.Status202Accepted => _status202Accepted ??= new(StatusCodes.Status202Accepted),
+            StatusCodes.Status203NonAuthoritative => _status203NonAuthoritative ??= new(StatusCodes.Status203NonAuthoritative),
+            StatusCodes.Status204NoContent => _status204NoContent ??= new(StatusCodes.Status204NoContent),
+            StatusCodes.Status205ResetContent => _status205ResetContent ??= new(StatusCodes.Status205ResetContent),
+            StatusCodes.Status206PartialContent => _status206PartialContent ??= new(StatusCodes.Status206PartialContent),
+            StatusCodes.Status207MultiStatus => _status207MultiStatus ??= new(StatusCodes.Status207MultiStatus),
+            StatusCodes.Status208AlreadyReported => _status208AlreadyReported ??= new(StatusCodes.Status208AlreadyReported),
+            StatusCodes.Status226IMUsed => _status226IMUsed ??= new(StatusCodes.Status226IMUsed),
+            StatusCodes.Status300MultipleChoices => _status300MultipleChoices ??= new(StatusCodes.Status300MultipleChoices),
+            StatusCodes.Status301MovedPermanently => _status301MovedPermanently ??= new(StatusCodes.Status301MovedPermanently),
+            StatusCodes.Status302Found => _status302Found ??= new(StatusCodes.Status302Found),
+            StatusCodes.Status303SeeOther => _status303SeeOther ??= new(StatusCodes.Status303SeeOther),
+            StatusCodes.Status304NotModified => _status304NotModified ??= new(StatusCodes.Status304NotModified),
+            StatusCodes.Status305UseProxy => _status305UseProxy ??= new(StatusCodes.Status305UseProxy),
+            StatusCodes.Status306SwitchProxy => _status306SwitchProxy ??= new(StatusCodes.Status306SwitchProxy),
+            StatusCodes.Status307TemporaryRedirect => _status307TemporaryRedirect ??= new(StatusCodes.Status307TemporaryRedirect),
+            StatusCodes.Status308PermanentRedirect => _status308PermanentRedirect ??= new(StatusCodes.Status308PermanentRedirect),
+            StatusCodes.Status400BadRequest => _status400BadRequest ??= new(StatusCodes.Status400BadRequest),
+            StatusCodes.Status401Unauthorized => _status401Unauthorized ??= new(StatusCodes.Status401Unauthorized),
+            StatusCodes.Status402PaymentRequired => _status402PaymentRequired ??= new(StatusCodes.Status402PaymentRequired),
+            StatusCodes.Status403Forbidden => _status403Forbidden ??= new(StatusCodes.Status403Forbidden),
+            StatusCodes.Status404NotFound => _status404NotFound ??= new(StatusCodes.Status404NotFound),
+            StatusCodes.Status405MethodNotAllowed => _status405MethodNotAllowed ??= new(StatusCodes.Status405MethodNotAllowed),
+            StatusCodes.Status406NotAcceptable => _status406NotAcceptable ??= new(StatusCodes.Status406NotAcceptable),
+            StatusCodes.Status407ProxyAuthenticationRequired => _status407ProxyAuthenticationRequired ??= new(StatusCodes.Status407ProxyAuthenticationRequired),
+            StatusCodes.Status408RequestTimeout => _status408RequestTimeout ??= new(StatusCodes.Status408RequestTimeout),
+            StatusCodes.Status409Conflict => _status409Conflict ??= new(StatusCodes.Status409Conflict),
+            StatusCodes.Status410Gone => _status410Gone ??= new(StatusCodes.Status410Gone),
+            StatusCodes.Status411LengthRequired => _status411LengthRequired ??= new(StatusCodes.Status411LengthRequired),
+            StatusCodes.Status412PreconditionFailed => _status412PreconditionFailed ??= new(StatusCodes.Status412PreconditionFailed),
+            StatusCodes.Status413RequestEntityTooLarge => _status413RequestEntityTooLarge ??= new(StatusCodes.Status413RequestEntityTooLarge),
+            StatusCodes.Status414RequestUriTooLong => _status414RequestUriTooLong ??= new(StatusCodes.Status414RequestUriTooLong),
+            StatusCodes.Status415UnsupportedMediaType => _status415UnsupportedMediaType ??= new(StatusCodes.Status415UnsupportedMediaType),
+            StatusCodes.Status416RequestedRangeNotSatisfiable => _status416RequestedRangeNotSatisfiable ??= new(StatusCodes.Status416RequestedRangeNotSatisfiable),
+            StatusCodes.Status417ExpectationFailed => _status417ExpectationFailed ??= new(StatusCodes.Status417ExpectationFailed),
+            StatusCodes.Status418ImATeapot => _status418ImATeapot ??= new(StatusCodes.Status418ImATeapot),
+            StatusCodes.Status419AuthenticationTimeout => _status419AuthenticationTimeout ??= new(StatusCodes.Status419AuthenticationTimeout),
+            StatusCodes.Status421MisdirectedRequest => _status421MisdirectedRequest ??= new(StatusCodes.Status421MisdirectedRequest),
+            StatusCodes.Status422UnprocessableEntity => _status422UnprocessableEntity ??= new(StatusCodes.Status422UnprocessableEntity),
+            StatusCodes.Status423Locked => _status423Locked ??= new(StatusCodes.Status423Locked),
+            StatusCodes.Status424FailedDependency => _status424FailedDependency ??= new(StatusCodes.Status424FailedDependency),
+            StatusCodes.Status426UpgradeRequired => _status426UpgradeRequired ??= new(StatusCodes.Status426UpgradeRequired),
+            StatusCodes.Status428PreconditionRequired => _status428PreconditionRequired ??= new(StatusCodes.Status428PreconditionRequired),
+            StatusCodes.Status429TooManyRequests => _status429TooManyRequests ??= new(StatusCodes.Status429TooManyRequests),
+            StatusCodes.Status431RequestHeaderFieldsTooLarge => _status431RequestHeaderFieldsTooLarge ??= new(StatusCodes.Status431RequestHeaderFieldsTooLarge),
+            StatusCodes.Status451UnavailableForLegalReasons => _status451UnavailableForLegalReasons ??= new(StatusCodes.Status451UnavailableForLegalReasons),
+            StatusCodes.Status500InternalServerError => _status500InternalServerError ??= new(StatusCodes.Status500InternalServerError),
+            StatusCodes.Status501NotImplemented => _status501NotImplemented ??= new(StatusCodes.Status501NotImplemented),
+            StatusCodes.Status502BadGateway => _status502BadGateway ??= new(StatusCodes.Status502BadGateway),
+            StatusCodes.Status503ServiceUnavailable => _status503ServiceUnavailable ??= new(StatusCodes.Status503ServiceUnavailable),
+            StatusCodes.Status504GatewayTimeout => _status504GatewayTimeout ??= new(StatusCodes.Status504GatewayTimeout),
+            StatusCodes.Status505HttpVersionNotsupported => _status505HttpVersionNotsupported ??= new(StatusCodes.Status505HttpVersionNotsupported),
+            StatusCodes.Status506VariantAlsoNegotiates => _status506VariantAlsoNegotiates ??= new(StatusCodes.Status506VariantAlsoNegotiates),
+            StatusCodes.Status507InsufficientStorage => _status507InsufficientStorage ??= new(StatusCodes.Status507InsufficientStorage),
+            StatusCodes.Status508LoopDetected => _status508LoopDetected ??= new(StatusCodes.Status508LoopDetected),
+            StatusCodes.Status510NotExtended => _status510NotExtended ??= new(StatusCodes.Status510NotExtended),
+            StatusCodes.Status511NetworkAuthenticationRequired => _status511NetworkAuthenticationRequired ??= new(StatusCodes.Status511NetworkAuthenticationRequired),
+            _ => new StatusCodeHttpResult(statusCode),
+        };
+    }
+}

+ 106 - 0
src/Http/Http.Results/src/ResultsCache.StatusCodes.tt

@@ -0,0 +1,106 @@
+<#@ template debug="false" hostspecific="false" language="C#" #>
+<#@ assembly name="System.Core" #>
+<#@ output extension=".cs" #>
+<#
+	/// A collection of constants for
+	/// <see href="http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml" >HTTP status codes</see >.
+	var statusCodes = new[]
+	{
+		new { StatusCode = 101, Name = "101SwitchingProtocols"},
+		new { StatusCode = 102, Name = "102Processing"},
+		new { StatusCode = 200, Name = "200OK"},
+		new { StatusCode = 201, Name = "201Created"},
+		new { StatusCode = 202, Name = "202Accepted"},
+		new { StatusCode = 203, Name = "203NonAuthoritative"},
+		new { StatusCode = 204, Name = "204NoContent"},
+		new { StatusCode = 205, Name = "205ResetContent"},
+		new { StatusCode = 206, Name = "206PartialContent"},
+		new { StatusCode = 207, Name = "207MultiStatus"},
+		new { StatusCode = 208, Name = "208AlreadyReported"},
+		new { StatusCode = 226, Name = "226IMUsed"},
+		new { StatusCode = 300, Name = "300MultipleChoices"},
+		new { StatusCode = 301, Name = "301MovedPermanently"},
+		new { StatusCode = 302, Name = "302Found"},
+		new { StatusCode = 303, Name = "303SeeOther"},
+		new { StatusCode = 304, Name = "304NotModified"},
+		new { StatusCode = 305, Name = "305UseProxy"},
+		new { StatusCode = 306, Name = "306SwitchProxy"},
+		new { StatusCode = 307, Name = "307TemporaryRedirect"},
+		new { StatusCode = 308, Name = "308PermanentRedirect"},
+		new { StatusCode = 400, Name = "400BadRequest"},
+		new { StatusCode = 401, Name = "401Unauthorized"},
+		new { StatusCode = 402, Name = "402PaymentRequired"},
+		new { StatusCode = 403, Name = "403Forbidden"},
+		new { StatusCode = 404, Name = "404NotFound"},
+		new { StatusCode = 405, Name = "405MethodNotAllowed"},
+		new { StatusCode = 406, Name = "406NotAcceptable"},
+		new { StatusCode = 407, Name = "407ProxyAuthenticationRequired"},
+		new { StatusCode = 408, Name = "408RequestTimeout"},
+		new { StatusCode = 409, Name = "409Conflict"},
+		new { StatusCode = 410, Name = "410Gone"},
+		new { StatusCode = 411, Name = "411LengthRequired"},
+		new { StatusCode = 412, Name = "412PreconditionFailed"},
+		new { StatusCode = 413, Name = "413RequestEntityTooLarge"},
+		new { StatusCode = 414, Name = "414RequestUriTooLong"},
+		new { StatusCode = 415, Name = "415UnsupportedMediaType"},
+		new { StatusCode = 416, Name = "416RequestedRangeNotSatisfiable"},
+		new { StatusCode = 417, Name = "417ExpectationFailed"},
+		new { StatusCode = 418, Name = "418ImATeapot"},
+		new { StatusCode = 419, Name = "419AuthenticationTimeout"},
+		new { StatusCode = 421, Name = "421MisdirectedRequest"},
+		new { StatusCode = 422, Name = "422UnprocessableEntity"},
+		new { StatusCode = 423, Name = "423Locked"},
+		new { StatusCode = 424, Name = "424FailedDependency"},
+		new { StatusCode = 426, Name = "426UpgradeRequired"},
+		new { StatusCode = 428, Name = "428PreconditionRequired"},
+		new { StatusCode = 429, Name = "429TooManyRequests"},
+		new { StatusCode = 431, Name = "431RequestHeaderFieldsTooLarge"},
+		new { StatusCode = 451, Name = "451UnavailableForLegalReasons"},
+		new { StatusCode = 500, Name = "500InternalServerError"},
+		new { StatusCode = 501, Name = "501NotImplemented"},
+		new { StatusCode = 502, Name = "502BadGateway"},
+		new { StatusCode = 503, Name = "503ServiceUnavailable"},
+		new { StatusCode = 504, Name = "504GatewayTimeout"},
+		new { StatusCode = 505, Name = "505HttpVersionNotsupported"},
+		new { StatusCode = 506, Name = "506VariantAlsoNegotiates"},
+		new { StatusCode = 507, Name = "507InsufficientStorage"},
+		new { StatusCode = 508, Name = "508LoopDetected"},
+		new { StatusCode = 510, Name = "510NotExtended"},
+		new { StatusCode = 511, Name = "511NetworkAuthenticationRequired"},
+	}; 
+#>
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// <auto-generated />
+
+#nullable enable
+
+using System.CodeDom.Compiler;
+
+namespace Microsoft.AspNetCore.Http;
+
+[GeneratedCode("TextTemplatingFileGenerator", "")]
+internal partial class ResultsCache
+{
+<# foreach (var statusCode in statusCodes) { #>
+    private static StatusCodeHttpResult? _status<#= statusCode.Name #>;
+<# } #>
+
+    internal static StatusCodeHttpResult StatusCode(int statusCode)
+    {
+        if (statusCode is (< 100) or (> 599))
+        {
+            // No HTTP status code assigned outside the 100..599 range
+            // so, it will not be available in the cache
+            return new StatusCodeHttpResult(statusCode);
+        }
+
+        return statusCode switch
+        {
+<# foreach (var statusCode in statusCodes) { #>
+            StatusCodes.Status<#= statusCode.Name #> => _status<#= statusCode.Name #> ??= new(StatusCodes.Status<#= statusCode.Name #>),
+<# } #>
+            _ => new StatusCodeHttpResult(statusCode),
+        };
+    }
+}

+ 15 - 0
src/Http/Http.Results/src/ResultsCache.cs

@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.Http;
+
+internal static partial class ResultsCache
+{
+    public static NotFoundObjectHttpResult NotFound { get; } = new(null);
+    public static UnauthorizedHttpResult Unauthorized { get; } = new();
+    public static BadRequestObjectHttpResult BadRequest { get; } = new(null);
+    public static ConflictObjectHttpResult Conflict { get; } = new(null);
+    public static NoContentHttpResult NoContent { get; } = new();
+    public static OkObjectHttpResult Ok { get; } = new(null);
+    public static UnprocessableEntityObjectHttpResult UnprocessableEntity { get; } = new(null);
+}

+ 5 - 3
src/Http/Http.Results/test/Microsoft.AspNetCore.Http.Results.Tests.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -8,12 +8,14 @@
     <Reference Include="Microsoft.AspNetCore.Http" />
     <Reference Include="Microsoft.AspNetCore.Http.Results" />
     <Reference Include="Microsoft.Extensions.DependencyInjection" />
+    <Reference Include="Mono.TextTemplating" />
 
     <ProjectReference Include="..\tools\ResultsOfTGenerator\ResultsOfTGenerator.csproj" />
 
     <Compile Include="$(SharedSourceRoot)ResultsTests\*.cs" LinkBase="Shared" />
-
-    <Content Include="..\src\ResultsOfT.cs" LinkBase="Shared\GeneratedContent" CopyToOutputDirectory="PreserveNewest" />
+    <Content Include="$(HttpResultsSourceRoot)\ResultsCache.StatusCodes.tt" LinkBase="Shared\GeneratedContent" CopyToOutputDirectory="PreserveNewest" />
+    <Content Include="$(HttpResultsSourceRoot)\ResultsCache.StatusCodes.cs" LinkBase="Shared\GeneratedContent" CopyToOutputDirectory="PreserveNewest" />
+    <Content Include="$(HttpResultsSourceRoot)\ResultsOfT.cs" LinkBase="Shared\GeneratedContent" CopyToOutputDirectory="PreserveNewest" />
     <Compile Update="ResultsOfTTests.Generated.cs" Link="Shared\GeneratedContent\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
   </ItemGroup>
 

+ 24 - 0
src/Http/Http.Results/test/ResultsCacheTests.cs

@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.Http.Results.Tests;
+
+using Mono.TextTemplating;
+
+public class ResultsCacheTests
+{
+    [Fact]
+    public void GeneratedCodeIsUpToDate()
+    {
+        var currentContentPath = Path.Combine(AppContext.BaseDirectory, "Shared", "GeneratedContent", "ResultsCache.StatusCodes.cs");
+        var templatePath = Path.Combine(AppContext.BaseDirectory, "Shared", "GeneratedContent", "ResultsCache.StatusCodes.tt");
+
+        var generator = new TemplateGenerator();
+        var compiledTemplate = generator.CompileTemplate(File.ReadAllText(templatePath));
+
+        var generatedContent = compiledTemplate.Process();
+        var currentContent = File.ReadAllText(currentContentPath);
+
+        Assert.Equal(currentContent, generatedContent);
+    }
+}

+ 7 - 0
src/Http/README.md

@@ -25,6 +25,13 @@ The following contains a description of each sub-directory in the `Http` directo
 
 ## Development Setup
 
+To run the code generation using [T4 Text Templates](https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates), you can use an IDE that supports it (eg. Visual Studio or JetBrains Rider) or install the cross-platform open-source dotnet tool [Mono/T4](https://github.com/mono/t4).
+
+```powershell
+> dotnet tool install -g dotnet-t4
+> t4 Http.Results\ResultsCache.StatusCodes.tt
+```
+
 ### Build
 
 To build this specific project from source, follow the instructions [on building the project](../../docs/BuildFromSource.md#step-3-build-the-repo).