11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4+ using System ;
45using System . Text ;
56
67namespace Microsoft . AspNetCore . Http . Generators . StaticRouteHandlerModel . Emitters ;
@@ -9,22 +10,22 @@ internal static class EndpointParameterEmitter
910 internal static string EmitSpecialParameterPreparation ( this EndpointParameter endpointParameter )
1011 {
1112 return $ """
12- var { endpointParameter . Name } _local = { endpointParameter . AssigningCode } ;
13+ var { endpointParameter . EmitHandlerArgument ( ) } = { endpointParameter . AssigningCode } ;
1314""" ;
1415 }
1516
16- internal static string EmitQueryParameterPreparation ( this EndpointParameter endpointParameter )
17+ internal static string EmitQueryOrHeaderParameterPreparation ( this EndpointParameter endpointParameter )
1718 {
1819 var builder = new StringBuilder ( ) ;
19-
20- // Preamble for diagnostics purposes.
2120 builder . AppendLine ( $ """
2221 { endpointParameter . EmitParameterDiagnosticComment ( ) }
2322""" ) ;
2423
25- // Grab raw input from HttpContext.
24+ var assigningCode = endpointParameter . Source is EndpointParameterSource . Header
25+ ? $ "httpContext.Request.Headers[\" { endpointParameter . Name } \" ]"
26+ : $ "httpContext.Request.Query[\" { endpointParameter . Name } \" ]";
2627 builder . AppendLine ( $$ """
27- var {{ endpointParameter . Name }} _raw = {{ endpointParameter . AssigningCode }} ;
28+ var {{ endpointParameter . EmitAssigningCodeResult ( ) }} = {{ assigningCode }} ;
2829""" ) ;
2930
3031 // If we are not optional, then at this point we can just assign the string value to the handler argument,
@@ -34,35 +35,103 @@ internal static string EmitQueryParameterPreparation(this EndpointParameter endp
3435 if ( endpointParameter . IsOptional )
3536 {
3637 builder . AppendLine ( $$ """
37- var {{ endpointParameter . HandlerArgument }} = {{ endpointParameter . Name }} _raw .Count > 0 ? {{ endpointParameter . Name }} _raw .ToString() : null;
38+ var {{ endpointParameter . EmitHandlerArgument ( ) }} = {{ endpointParameter . EmitAssigningCodeResult ( ) }} .Count > 0 ? {{ endpointParameter . EmitAssigningCodeResult ( ) }} .ToString() : null;
3839""" ) ;
3940 }
4041 else
4142 {
4243 builder . AppendLine ( $$ """
43- if (StringValues.IsNullOrEmpty({{ endpointParameter . Name }} _raw ))
44+ if (StringValues.IsNullOrEmpty({{ endpointParameter . EmitAssigningCodeResult ( ) }} ))
4445 {
4546 wasParamCheckFailure = true;
4647 }
47- var {{ endpointParameter . HandlerArgument }} = {{ endpointParameter . Name }} _raw .ToString();
48+ var {{ endpointParameter . EmitHandlerArgument ( ) }} = {{ endpointParameter . EmitAssigningCodeResult ( ) }} .ToString();
4849""" ) ;
4950 }
5051
5152 return builder . ToString ( ) ;
5253 }
5354
54- internal static string EmitJsonBodyParameterPreparationString ( this EndpointParameter endpointParameter )
55+ internal static string EmitRouteParameterPreparation ( this EndpointParameter endpointParameter )
5556 {
5657 var builder = new StringBuilder ( ) ;
58+ builder . AppendLine ( $ """
59+ { endpointParameter . EmitParameterDiagnosticComment ( ) }
60+ """ ) ;
5761
58- // Preamble for diagnostics purposes.
62+ // Throw an exception of if the route parameter name that was specific in the `FromRoute`
63+ // attribute or in the parameter name does not appear in the actual route.
64+ builder . AppendLine ( $$ """
65+ if (options?.RouteParameterNames?.Contains("{{ endpointParameter . Name }} ", StringComparer.OrdinalIgnoreCase) != true)
66+ {
67+ throw new InvalidOperationException($"'{{ endpointParameter . Name }} ' is not a route parameter.");
68+ }
69+ """ ) ;
70+
71+ var assigningCode = $ "httpContext.Request.RouteValues[\" { endpointParameter . Name } \" ]?.ToString()";
72+ builder . AppendLine ( $$ """
73+ var {{ endpointParameter . EmitAssigningCodeResult ( ) }} = {{ assigningCode }} ;
74+ """ ) ;
75+
76+ if ( ! endpointParameter . IsOptional )
77+ {
78+ builder . AppendLine ( $$ """
79+ if ({{ endpointParameter . EmitAssigningCodeResult ( ) }} == null)
80+ {
81+ wasParamCheckFailure = true;
82+ }
83+ """ ) ;
84+ }
85+ builder . AppendLine ( $ """
86+ var { endpointParameter . EmitHandlerArgument ( ) } = { endpointParameter . EmitAssigningCodeResult ( ) } ;
87+ """ ) ;
88+
89+ return builder . ToString ( ) ;
90+ }
91+
92+ internal static string EmitRouteOrQueryParameterPreparation ( this EndpointParameter endpointParameter )
93+ {
94+ var builder = new StringBuilder ( ) ;
5995 builder . AppendLine ( $ """
6096 { endpointParameter . EmitParameterDiagnosticComment ( ) }
6197""" ) ;
6298
63- // Grab raw input from HttpContext.
99+ var parameterName = endpointParameter . Name ;
100+ var assigningCode = $@ "options?.RouteParameterNames?.Contains(""{ parameterName } "", StringComparer.OrdinalIgnoreCase) == true";
101+ assigningCode += $@ "? new StringValues(httpContext.Request.RouteValues[$""{ parameterName } ""]?.ToString())";
102+ assigningCode += $@ ": httpContext.Request.Query[$""{ parameterName } ""];";
103+
64104 builder . AppendLine ( $$ """
65- var (isSuccessful, {{ endpointParameter . Name }} _local) = {{ endpointParameter . AssigningCode }} ;
105+ var {{ endpointParameter . EmitAssigningCodeResult ( ) }} = {{ assigningCode }} ;
106+ """ ) ;
107+
108+ if ( ! endpointParameter . IsOptional )
109+ {
110+ builder . AppendLine ( $$ """
111+ if ({{ endpointParameter . EmitAssigningCodeResult ( ) }} is StringValues { Count: 0 })
112+ {
113+ wasParamCheckFailure = true;
114+ }
115+ """ ) ;
116+ }
117+
118+ builder . AppendLine ( $ """
119+ var { endpointParameter . EmitHandlerArgument ( ) } = { endpointParameter . EmitAssigningCodeResult ( ) } ;
120+ """ ) ;
121+
122+ return builder . ToString ( ) ;
123+ }
124+
125+ internal static string EmitJsonBodyParameterPreparationString ( this EndpointParameter endpointParameter )
126+ {
127+ var builder = new StringBuilder ( ) ;
128+ builder . AppendLine ( $ """
129+ { endpointParameter . EmitParameterDiagnosticComment ( ) }
130+ """ ) ;
131+
132+ var assigningCode = $ "await GeneratedRouteBuilderExtensionsCore.TryResolveBody<{ endpointParameter . Type . ToDisplayString ( EmitterConstants . DisplayFormat ) } >(httpContext, { ( endpointParameter . IsOptional ? "true" : "false" ) } )";
133+ builder . AppendLine ( $$ """
134+ var (isSuccessful, {{ endpointParameter . EmitHandlerArgument ( ) }} ) = {{ assigningCode }} ;
66135""" ) ;
67136
68137 // If binding from the JSON body fails, we exit early. Don't
@@ -88,14 +157,32 @@ internal static string EmitServiceParameterPreparation(this EndpointParameter en
88157""" ) ;
89158
90159 // Requiredness checks for services are handled by the distinction
91- // between GetRequiredService and GetService in the AssigningCode.
160+ // between GetRequiredService and GetService in the assigningCode.
161+ // Unlike other scenarios, this will result in an exception being thrown
162+ // at runtime.
163+ var assigningCode = endpointParameter . IsOptional ?
164+ $ "httpContext.RequestServices.GetService<{ endpointParameter . Type } >();" :
165+ $ "httpContext.RequestServices.GetRequiredService<{ endpointParameter . Type } >()";
166+
92167 builder . AppendLine ( $$ """
93- var {{ endpointParameter . HandlerArgument }} = {{ endpointParameter . AssigningCode }} ;
168+ var {{ endpointParameter . EmitHandlerArgument ( ) }} = {{ assigningCode }} ;
94169""" ) ;
95170
96171 return builder . ToString ( ) ;
97172 }
98173
99174 private static string EmitParameterDiagnosticComment ( this EndpointParameter endpointParameter ) =>
100- $ "// Endpoint Parameter: { endpointParameter . Name } (Type = { endpointParameter . Type } , IsOptional = { endpointParameter . IsOptional } , Source = { endpointParameter . Source } )";
175+ $ "// Endpoint Parameter: { endpointParameter . Name } (Type = { endpointParameter . Type . ToDisplayString ( EmitterConstants . DisplayFormat ) } , IsOptional = { endpointParameter . IsOptional } , Source = { endpointParameter . Source } )";
176+
177+ private static string EmitHandlerArgument ( this EndpointParameter endpointParameter ) => $ "{ endpointParameter . Name } _local";
178+ private static string EmitAssigningCodeResult ( this EndpointParameter endpointParameter ) => $ "{ endpointParameter . Name } _raw";
179+
180+ public static string EmitArgument ( this EndpointParameter endpointParameter ) => endpointParameter . Source switch
181+ {
182+ EndpointParameterSource . JsonBody or EndpointParameterSource . Route or EndpointParameterSource . RouteOrQuery => endpointParameter . IsOptional
183+ ? endpointParameter . EmitHandlerArgument ( )
184+ : $ "{ endpointParameter . EmitHandlerArgument ( ) } !",
185+ EndpointParameterSource . Unknown => throw new Exception ( "Unreachable!" ) ,
186+ _ => endpointParameter . EmitHandlerArgument ( )
187+ } ;
101188}
0 commit comments