-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Follow up on IParsable changes and update tests #47102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4139cea
71c9dcc
aba9336
80ce3c6
39f2529
131dbb2
ddb8065
1da2442
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,97 @@ internal static class RequestDelegateGeneratorSources | |
|
||
public static string GeneratedCodeAttribute => $@"[System.CodeDom.Compiler.GeneratedCodeAttribute(""{typeof(RequestDelegateGeneratorSources).Assembly.FullName}"", ""{typeof(RequestDelegateGeneratorSources).Assembly.GetName().Version}"")]"; | ||
|
||
public static string GetGeneratedRouteBuilderExtensionsSource(string genericThunks, string thunks, string endpoints) => $$""" | ||
public static string TryResolveBodyAsyncMethod => """ | ||
private static async ValueTask<(bool, T?)> TryResolveBodyAsync<T>(HttpContext httpContext, bool allowEmpty) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these strings be cut over to CodeWriter implementations. Given where these get emitted, we can pretty easily predict the indentation requirements but really just asking for the sake of consistency since they are multi-line blocks. |
||
{ | ||
var feature = httpContext.Features.Get<Microsoft.AspNetCore.Http.Features.IHttpRequestBodyDetectionFeature>(); | ||
|
||
if (feature?.CanHaveBody == true) | ||
{ | ||
if (!httpContext.Request.HasJsonContentType()) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; | ||
return (false, default); | ||
} | ||
try | ||
{ | ||
var bodyValue = await httpContext.Request.ReadFromJsonAsync<T>(); | ||
if (!allowEmpty && bodyValue == null) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; | ||
return (false, bodyValue); | ||
} | ||
return (true, bodyValue); | ||
} | ||
catch (IOException) | ||
{ | ||
return (false, default); | ||
} | ||
catch (System.Text.Json.JsonException) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; | ||
return (false, default); | ||
} | ||
} | ||
else if (!allowEmpty) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; | ||
} | ||
|
||
return (false, default); | ||
} | ||
"""; | ||
|
||
public static string TryParseExplicitMethod => """ | ||
private static bool TryParseExplicit<T>(string? s, IFormatProvider? provider, [MaybeNullWhen(returnValue: false)] out T result) where T: IParsable<T> | ||
=> T.TryParse(s, provider, out result); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😲 does RDG now support something RDF doesn't? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, this was part of the original There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should file an issue to support it on both. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
"""; | ||
|
||
public static string TryResolveJsonBodyOrServiceAsyncMethod => """ | ||
private static ValueTask<(bool, T?)> TryResolveJsonBodyOrServiceAsync<T>(HttpContext httpContext, bool isOptional, IServiceProviderIsService? serviceProviderIsService = null) | ||
{ | ||
if (serviceProviderIsService is not null) | ||
{ | ||
if (serviceProviderIsService.IsService(typeof(T))) | ||
{ | ||
return new ValueTask<(bool, T?)>((true, httpContext.RequestServices.GetService<T>())); | ||
} | ||
} | ||
return TryResolveBodyAsync<T>(httpContext, isOptional); | ||
} | ||
"""; | ||
|
||
public static string BindAsyncMethod => """ | ||
private static ValueTask<T?> BindAsync<T>(HttpContext context, ParameterInfo parameter) | ||
where T : class, IBindableFromHttpContext<T> | ||
{ | ||
return T.BindAsync(context, parameter); | ||
} | ||
"""; | ||
|
||
public static string ResolveFromRouteOrQueryMethod => """ | ||
private static Func<HttpContext, StringValues> ResolveFromRouteOrQuery(string parameterName, IEnumerable<string>? routeParameterNames) | ||
{ | ||
return routeParameterNames?.Contains(parameterName, StringComparer.OrdinalIgnoreCase) == true | ||
? (httpContext) => new StringValues((string?)httpContext.Request.RouteValues[parameterName]) | ||
: (httpContext) => httpContext.Request.Query[parameterName]; | ||
} | ||
"""; | ||
|
||
public static string WriteToResponseAsyncMethod => """ | ||
private static Task WriteToResponseAsync<T>(T? value, HttpContext httpContext, JsonTypeInfo<T> jsonTypeInfo, JsonSerializerOptions options) | ||
{ | ||
var runtimeType = value?.GetType(); | ||
if (runtimeType is null || jsonTypeInfo.Type == runtimeType || jsonTypeInfo.PolymorphismOptions is not null) | ||
{ | ||
return httpContext.Response.WriteAsJsonAsync(value!, jsonTypeInfo); | ||
} | ||
|
||
return httpContext.Response.WriteAsJsonAsync(value!, options.GetTypeInfo(runtimeType)); | ||
} | ||
"""; | ||
|
||
public static string GetGeneratedRouteBuilderExtensionsSource(string genericThunks, string thunks, string endpoints, string helperMethods) => $$""" | ||
{{SourceHeader}} | ||
|
||
namespace Microsoft.AspNetCore.Builder | ||
|
@@ -107,87 +197,7 @@ private static Task ExecuteObjectResult(object? obj, HttpContext httpContext) | |
} | ||
} | ||
|
||
private static Func<HttpContext, StringValues> ResolveFromRouteOrQuery(string parameterName, IEnumerable<string>? routeParameterNames) | ||
{ | ||
return routeParameterNames?.Contains(parameterName, StringComparer.OrdinalIgnoreCase) == true | ||
? (httpContext) => new StringValues((string?)httpContext.Request.RouteValues[parameterName]) | ||
: (httpContext) => httpContext.Request.Query[parameterName]; | ||
} | ||
|
||
private static Task WriteToResponseAsync<T>(T? value, HttpContext httpContext, JsonTypeInfo<T> jsonTypeInfo, JsonSerializerOptions options) | ||
{ | ||
var runtimeType = value?.GetType(); | ||
if (runtimeType is null || jsonTypeInfo.Type == runtimeType || jsonTypeInfo.PolymorphismOptions is not null) | ||
{ | ||
return httpContext.Response.WriteAsJsonAsync(value!, jsonTypeInfo); | ||
} | ||
|
||
return httpContext.Response.WriteAsJsonAsync(value!, options.GetTypeInfo(runtimeType)); | ||
} | ||
|
||
private static async ValueTask<(bool, T?)> TryResolveBodyAsync<T>(HttpContext httpContext, bool allowEmpty) | ||
{ | ||
var feature = httpContext.Features.Get<Microsoft.AspNetCore.Http.Features.IHttpRequestBodyDetectionFeature>(); | ||
|
||
if (feature?.CanHaveBody == true) | ||
{ | ||
if (!httpContext.Request.HasJsonContentType()) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType; | ||
return (false, default); | ||
} | ||
try | ||
{ | ||
var bodyValue = await httpContext.Request.ReadFromJsonAsync<T>(); | ||
if (!allowEmpty && bodyValue == null) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; | ||
return (false, bodyValue); | ||
} | ||
return (true, bodyValue); | ||
} | ||
catch (IOException) | ||
{ | ||
return (false, default); | ||
} | ||
catch (System.Text.Json.JsonException) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; | ||
return (false, default); | ||
} | ||
} | ||
else if (!allowEmpty) | ||
{ | ||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest; | ||
} | ||
|
||
return (false, default); | ||
} | ||
|
||
private static ValueTask<T?> BindAsync<T>(HttpContext context, ParameterInfo parameter) | ||
where T : class, IBindableFromHttpContext<T> | ||
{ | ||
return T.BindAsync(context, parameter); | ||
} | ||
|
||
private static ValueTask<(bool, T?)> TryResolveJsonBodyOrServiceAsync<T>(HttpContext httpContext, bool isOptional, IServiceProviderIsService? serviceProviderIsService = null) | ||
{ | ||
if (serviceProviderIsService is not null) | ||
{ | ||
if (serviceProviderIsService.IsService(typeof(T))) | ||
{ | ||
return new ValueTask<(bool, T?)>((true, httpContext.RequestServices.GetService<T>())); | ||
} | ||
} | ||
return TryResolveBodyAsync<T>(httpContext, isOptional); | ||
} | ||
} | ||
|
||
{{GeneratedCodeAttribute}} | ||
file static class ParsableHelper<T> where T : IParsable<T> | ||
{ | ||
public static T Parse(string s, IFormatProvider? provider) => T.Parse(s, provider); | ||
public static bool TryParse(string? s, IFormatProvider? provider, [MaybeNullWhen(returnValue: false)] out T result) => T.TryParse(s, provider, out result); | ||
{{helperMethods}} | ||
} | ||
} | ||
"""; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// 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.Generators.StaticRouteHandlerModel.Emitters; | ||
|
||
internal sealed class EmitterContext | ||
{ | ||
public bool HasJsonBodyOrService { get; set; } | ||
public bool HasJsonBody { get; set; } | ||
public bool HasRouteOrQuery { get; set; } | ||
public bool HasBindAsync { get; set; } | ||
public bool HasParsable { get; set; } | ||
public bool HasJsonResponse { get; set; } | ||
} |
Uh oh!
There was an error while loading. Please reload this page.