Skip to content

Commit 11dafe0

Browse files
committed
Fixing bug in optional route parameter binding
1 parent 591f539 commit 11dafe0

File tree

3 files changed

+56
-15
lines changed

3 files changed

+56
-15
lines changed

sample/HttpTrigger-CSharp-CustomRoute/run.csx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class ProductInfo
1313

1414
public static ProductInfo Run(ProductInfo info, string category, int? id, TraceWriter log)
1515
{
16-
log.Info($"ProductInfo: Category={category} Id={id}");
16+
log.Info($"ProductInfo: Category={info.Category} Id={info.Id}");
1717
log.Info($"Parameters: category={category} id={id}");
1818

1919
return info;

src/WebJobs.Script.WebHost/WebScriptHostManager.cs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -347,25 +347,28 @@ public FunctionDescriptor GetHttpFunctionOrNull(HttpRequestMessage request)
347347
if (routeData != null)
348348
{
349349
_httpFunctions.TryGetValue(routeData.Route, out function);
350+
AddRouteDataToRequest(routeData, request);
351+
}
352+
353+
return function;
354+
}
350355

351-
Dictionary<string, object> routeDataValues = null;
352-
if (routeData.Values != null)
356+
internal static void AddRouteDataToRequest(IHttpRouteData routeData, HttpRequestMessage request)
357+
{
358+
if (routeData.Values != null)
359+
{
360+
Dictionary<string, object> routeDataValues = new Dictionary<string, object>();
361+
foreach (var pair in routeData.Values)
353362
{
354-
routeDataValues = new Dictionary<string, object>();
355-
foreach (var pair in routeData.Values)
356-
{
357-
// filter out any unspecified optional parameters
358-
if (pair.Value != RouteParameter.Optional)
359-
{
360-
routeDataValues.Add(pair.Key, pair.Value);
361-
}
362-
}
363+
// translate any unspecified optional parameters to null values
364+
// unspecified values still need to be included as part of binding data
365+
// for correct binding to occur
366+
var value = pair.Value != RouteParameter.Optional ? pair.Value : null;
367+
routeDataValues.Add(pair.Key, value);
363368
}
364369

365370
request.Properties.Add(ScriptConstants.AzureFunctionsHttpRouteDataKey, routeDataValues);
366-
}
367-
368-
return function;
371+
}
369372
}
370373

371374
protected override void OnInitializeConfig(ScriptHostConfiguration config)

test/WebJobs.Script.Tests/WebScriptHostManagerTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using System.Net.Http;
1111
using System.Text.RegularExpressions;
1212
using System.Threading.Tasks;
13+
using System.Web.Http;
14+
using System.Web.Http.Routing;
1315
using Microsoft.Azure.WebJobs.Host;
1416
using Microsoft.Azure.WebJobs.Script.Description;
1517
using Microsoft.Azure.WebJobs.Script.Tests;
@@ -194,6 +196,42 @@ public async Task OnTimeoutException_UsesToken_ManagerKeepsRunning()
194196
Assert.DoesNotContain(trace.Traces, t => t.Message == "A function timeout has occurred. Host is shutting down.");
195197
}
196198

199+
[Fact]
200+
public void AddRouteDataToRequest_DoesNotAddRequestProperty_WhenRouteDataNull()
201+
{
202+
var mockRouteData = new Mock<IHttpRouteData>(MockBehavior.Strict);
203+
IDictionary<string, object> values = null;
204+
mockRouteData.Setup(p => p.Values).Returns(values);
205+
HttpRequestMessage request = new HttpRequestMessage();
206+
207+
WebScriptHostManager.AddRouteDataToRequest(mockRouteData.Object, request);
208+
209+
Assert.False(request.Properties.ContainsKey(ScriptConstants.AzureFunctionsHttpRouteDataKey));
210+
}
211+
212+
[Fact]
213+
public void AddRouteDataToRequest_AddsRequestProperty_WhenRouteDataNotNull()
214+
{
215+
var mockRouteData = new Mock<IHttpRouteData>(MockBehavior.Strict);
216+
IDictionary<string, object> values = new Dictionary<string, object>
217+
{
218+
{ "p1", "abc" },
219+
{ "p2", 123 },
220+
{ "p3", null },
221+
{ "p4", RouteParameter.Optional }
222+
};
223+
mockRouteData.Setup(p => p.Values).Returns(values);
224+
HttpRequestMessage request = new HttpRequestMessage();
225+
226+
WebScriptHostManager.AddRouteDataToRequest(mockRouteData.Object, request);
227+
228+
var result = (IDictionary<string, object>)request.Properties[ScriptConstants.AzureFunctionsHttpRouteDataKey];
229+
Assert.Equal(result["p1"], "abc");
230+
Assert.Equal(result["p2"], 123);
231+
Assert.Equal(result["p3"], null);
232+
Assert.Equal(result["p4"], null);
233+
}
234+
197235
private async Task RunTimeoutExceptionTest(TraceWriter trace, bool handleCancellation)
198236
{
199237
TimeSpan gracePeriod = TimeSpan.FromMilliseconds(5000);

0 commit comments

Comments
 (0)