Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
<Compile Include="$(SharedSourceRoot)RoslynUtils\WellKnownTypes.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)RoslynUtils\WellKnownTypeData.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)RoslynUtils\SymbolExtensions.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)RoslynUtils\Bindability.cs" LinkBase="Shared"/>
<Compile Include="$(SharedSourceRoot)RoslynUtils\Parsability.cs" LinkBase="Shared"/>
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityMethod.cs" LinkBase="Shared"/>
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityHelper.cs" LinkBase="Shared"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
<Compile Include="$(SharedSourceRoot)RoslynUtils\WellKnownTypeData.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)RoslynUtils\WellKnownTypes.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)RoslynUtils\SymbolExtensions.cs" LinkBase="Shared" />
<Compile Include="$(SharedSourceRoot)RoslynUtils\Bindability.cs" LinkBase="Shared"/>
<Compile Include="$(SharedSourceRoot)RoslynUtils\Parsability.cs" LinkBase="Shared"/>
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityMethod.cs" LinkBase="Shared"/>
<Compile Include="$(SharedSourceRoot)RoslynUtils\ParsabilityHelper.cs" LinkBase="Shared"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace Microsoft.AspNetCore.Http.Generated
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Text;
using Microsoft.CodeAnalysis;

namespace Microsoft.AspNetCore.Http.Generators.StaticRouteHandlerModel.Emitters;
internal static class EndpointParameterEmitter
Expand Down Expand Up @@ -35,7 +36,7 @@ internal static string EmitQueryOrHeaderParameterPreparation(this EndpointParame
if (endpointParameter.IsOptional)
{
builder.AppendLine($$"""
var {{endpointParameter.EmitHandlerArgument()}} = {{endpointParameter.EmitAssigningCodeResult()}}.Count > 0 ? {{endpointParameter.EmitAssigningCodeResult()}}.ToString() : null;
var {{endpointParameter.Name}}_temp = {{endpointParameter.EmitAssigningCodeResult()}}.Count > 0 ? {{endpointParameter.Name}}_raw.ToString() : null;
""");
}
else
Expand All @@ -45,144 +46,166 @@ internal static string EmitQueryOrHeaderParameterPreparation(this EndpointParame
{
wasParamCheckFailure = true;
}
var {{endpointParameter.EmitHandlerArgument()}} = {{endpointParameter.EmitAssigningCodeResult()}}.ToString();
var {{endpointParameter.Name}}_temp = {{endpointParameter.EmitAssigningCodeResult()}}.ToString();
""");
}

builder.Append(endpointParameter.EmitParsingBlock());

return builder.ToString();
}

internal static string EmitRouteParameterPreparation(this EndpointParameter endpointParameter)
internal static string EmitParsingBlock(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();
builder.AppendLine($"""

if (endpointParameter.IsParsable)
{
var parsingBlock = endpointParameter.ParsingBlockEmitter($"{endpointParameter.Name}_temp", $"{endpointParameter.Name}_parsed_temp");
builder.AppendLine($$"""
{{parsingBlock}}
{{endpointParameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}} {{endpointParameter.EmitHandlerArgument()}} = {{endpointParameter.Name}}_parsed_temp!;
""");

}
else
{
builder.AppendLine($$"""
{{endpointParameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}} {{endpointParameter.EmitHandlerArgument()}} = {{endpointParameter.Name}}_temp!;
""");

}

return builder.ToString();
}

internal static string EmitRouteParameterPreparation(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();
builder.AppendLine($"""
{endpointParameter.EmitParameterDiagnosticComment()}
""");

// Throw an exception of if the route parameter name that was specific in the `FromRoute`
// attribute or in the parameter name does not appear in the actual route.
builder.AppendLine($$"""
// Throw an exception of if the route parameter name that was specific in the `FromRoute`
// attribute or in the parameter name does not appear in the actual route.
builder.AppendLine($$"""
if (options?.RouteParameterNames?.Contains("{{endpointParameter.Name}}", StringComparer.OrdinalIgnoreCase) != true)
{
throw new InvalidOperationException($"'{{endpointParameter.Name}}' is not a route parameter.");
}
""");

var assigningCode = $"httpContext.Request.RouteValues[\"{endpointParameter.Name}\"]?.ToString()";
builder.AppendLine($$"""
var assigningCode = $"httpContext.Request.RouteValues[\"{endpointParameter.Name}\"]?.ToString()";
builder.AppendLine($$"""
var {{endpointParameter.EmitAssigningCodeResult()}} = {{assigningCode}};
""");

if (!endpointParameter.IsOptional)
{
builder.AppendLine($$"""
if (!endpointParameter.IsOptional)
{
builder.AppendLine($$"""
if ({{endpointParameter.EmitAssigningCodeResult()}} == null)
{
wasParamCheckFailure = true;
}
""");
}
builder.AppendLine($"""
}
builder.AppendLine($"""
var {endpointParameter.EmitHandlerArgument()} = {endpointParameter.EmitAssigningCodeResult()};
""");

return builder.ToString();
}
return builder.ToString();
}

internal static string EmitRouteOrQueryParameterPreparation(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();
builder.AppendLine($"""
internal static string EmitRouteOrQueryParameterPreparation(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();
builder.AppendLine($"""
{endpointParameter.EmitParameterDiagnosticComment()}
""");

var parameterName = endpointParameter.Name;
var assigningCode = $@"options?.RouteParameterNames?.Contains(""{parameterName}"", StringComparer.OrdinalIgnoreCase) == true";
assigningCode += $@"? new StringValues(httpContext.Request.RouteValues[$""{parameterName}""]?.ToString())";
assigningCode += $@": httpContext.Request.Query[$""{parameterName}""];";
var parameterName = endpointParameter.Name;
var assigningCode = $@"options?.RouteParameterNames?.Contains(""{parameterName}"", StringComparer.OrdinalIgnoreCase) == true";
assigningCode += $@"? new StringValues(httpContext.Request.RouteValues[$""{parameterName}""]?.ToString())";
assigningCode += $@": httpContext.Request.Query[$""{parameterName}""];";

builder.AppendLine($$"""
builder.AppendLine($$"""
var {{endpointParameter.EmitAssigningCodeResult()}} = {{assigningCode}};
""");

if (!endpointParameter.IsOptional)
{
builder.AppendLine($$"""
if (!endpointParameter.IsOptional)
{
builder.AppendLine($$"""
if ({{endpointParameter.EmitAssigningCodeResult()}} is StringValues { Count: 0 })
{
wasParamCheckFailure = true;
}
""");
}
}

builder.AppendLine($"""
builder.AppendLine($"""
var {endpointParameter.EmitHandlerArgument()} = {endpointParameter.EmitAssigningCodeResult()};
""");

return builder.ToString();
}
return builder.ToString();
}

internal static string EmitJsonBodyParameterPreparationString(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();
builder.AppendLine($"""
internal static string EmitJsonBodyParameterPreparationString(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();
builder.AppendLine($"""
{endpointParameter.EmitParameterDiagnosticComment()}
""");

var assigningCode = $"await GeneratedRouteBuilderExtensionsCore.TryResolveBody<{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}>(httpContext, {(endpointParameter.IsOptional ? "true" : "false")})";
builder.AppendLine($$"""
var assigningCode = $"await GeneratedRouteBuilderExtensionsCore.TryResolveBody<{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}>(httpContext, {(endpointParameter.IsOptional ? "true" : "false")})";
builder.AppendLine($$"""
var (isSuccessful, {{endpointParameter.EmitHandlerArgument()}}) = {{assigningCode}};
""");

// If binding from the JSON body fails, we exit early. Don't
// set the status code here because assume it has been set by the
// TryResolveBody method.
builder.AppendLine("""
// If binding from the JSON body fails, we exit early. Don't
// set the status code here because assume it has been set by the
// TryResolveBody method.
builder.AppendLine("""
if (!isSuccessful)
{
return;
}
""");

return builder.ToString();
}
return builder.ToString();
}

internal static string EmitServiceParameterPreparation(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();
internal static string EmitServiceParameterPreparation(this EndpointParameter endpointParameter)
{
var builder = new StringBuilder();

// Preamble for diagnostics purposes.
builder.AppendLine($"""
// Preamble for diagnostics purposes.
builder.AppendLine($"""
{endpointParameter.EmitParameterDiagnosticComment()}
""");

// Requiredness checks for services are handled by the distinction
// between GetRequiredService and GetService in the assigningCode.
// Unlike other scenarios, this will result in an exception being thrown
// at runtime.
var assigningCode = endpointParameter.IsOptional ?
$"httpContext.RequestServices.GetService<{endpointParameter.Type}>();" :
$"httpContext.RequestServices.GetRequiredService<{endpointParameter.Type}>()";
// Requiredness checks for services are handled by the distinction
// between GetRequiredService and GetService in the assigningCode.
// Unlike other scenarios, this will result in an exception being thrown
// at runtime.
var assigningCode = endpointParameter.IsOptional ?
$"httpContext.RequestServices.GetService<{endpointParameter.Type}>();" :
$"httpContext.RequestServices.GetRequiredService<{endpointParameter.Type}>()";

builder.AppendLine($$"""
builder.AppendLine($$"""
var {{endpointParameter.EmitHandlerArgument()}} = {{assigningCode}};
""");

return builder.ToString();
}

private static string EmitParameterDiagnosticComment(this EndpointParameter endpointParameter) =>
$"// Endpoint Parameter: {endpointParameter.Name} (Type = {endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)}, IsOptional = {endpointParameter.IsOptional}, Source = {endpointParameter.Source})";
return builder.ToString();
}

private static string EmitHandlerArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.Name}_local";
private static string EmitAssigningCodeResult(this EndpointParameter endpointParameter) => $"{endpointParameter.Name}_raw";
private static string EmitParameterDiagnosticComment(this EndpointParameter endpointParameter) => $"// Endpoint Parameter: {endpointParameter.Name} (Type = {endpointParameter.Type}, IsOptional = {endpointParameter.IsOptional}, IsParsable = {endpointParameter.IsParsable}, Source = {endpointParameter.Source})";
private static string EmitHandlerArgument(this EndpointParameter endpointParameter) => $"{endpointParameter.Name}_local";
private static string EmitAssigningCodeResult(this EndpointParameter endpointParameter) => $"{endpointParameter.Name}_raw";

public static string EmitArgument(this EndpointParameter endpointParameter) => endpointParameter.Source switch
{
EndpointParameterSource.JsonBody or EndpointParameterSource.Route or EndpointParameterSource.RouteOrQuery => endpointParameter.IsOptional
? endpointParameter.EmitHandlerArgument()
: $"{endpointParameter.EmitHandlerArgument()}!",
EndpointParameterSource.Unknown => throw new Exception("Unreachable!"),
_ => endpointParameter.EmitHandlerArgument()
};
}
public static string EmitArgument(this EndpointParameter endpointParameter) => endpointParameter.Source switch
{
EndpointParameterSource.JsonBody or EndpointParameterSource.Route or EndpointParameterSource.RouteOrQuery => endpointParameter.IsOptional ? endpointParameter.EmitHandlerArgument() : $"{endpointParameter.EmitHandlerArgument()}!",
EndpointParameterSource.Unknown => throw new Exception("Unreachable!"),
_ => endpointParameter.EmitHandlerArgument()
};
}
Loading