Skip to content

Commit 9f766c1

Browse files
committed
Fixing dynamic support for http binding
1 parent ab6cb74 commit 9f766c1

File tree

12 files changed

+126
-4
lines changed

12 files changed

+126
-4
lines changed

WebJobs.Script.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bot", "Bot", "{98DBDE0D-364
403403
sample\Bot\run.js = sample\Bot\run.js
404404
EndProjectSection
405405
EndProject
406+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebHook-Generic-CSharp-Dynamic", "WebHook-Generic-CSharp-Dynamic", "{2F6EF597-6586-4F59-BBB6-BA7146FEFD27}"
407+
ProjectSection(SolutionItems) = preProject
408+
sample\WebHook-Generic-CSharp-Dynamic\function.json = sample\WebHook-Generic-CSharp-Dynamic\function.json
409+
sample\WebHook-Generic-CSharp-Dynamic\run.csx = sample\WebHook-Generic-CSharp-Dynamic\run.csx
410+
EndProjectSection
411+
EndProject
406412
Global
407413
GlobalSection(SolutionConfigurationPlatforms) = preSolution
408414
Debug|Any CPU = Debug|Any CPU
@@ -506,5 +512,6 @@ Global
506512
{ADE3C399-6996-4677-89A8-60EDA27BF9C2} = {FF9C0818-30D3-437A-A62D-7A61CA44F459}
507513
{EF36E33B-646C-4EF4-B0E2-517E14F6DC38} = {FF9C0818-30D3-437A-A62D-7A61CA44F459}
508514
{98DBDE0D-364C-4865-9A4A-B17410D771F6} = {FF9C0818-30D3-437A-A62D-7A61CA44F459}
515+
{2F6EF597-6586-4F59-BBB6-BA7146FEFD27} = {FF9C0818-30D3-437A-A62D-7A61CA44F459}
509516
EndGlobalSection
510517
EndGlobal
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"bindings": [
3+
{
4+
"type": "httpTrigger",
5+
"name": "payload",
6+
"direction": "in",
7+
"webHookType": "genericJson"
8+
},
9+
{
10+
"type": "http",
11+
"name": "res",
12+
"direction": "out"
13+
}
14+
]
15+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public static string Run(dynamic payload)
2+
{
3+
return $"Value: {payload.Value}";
4+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"keys": [
3+
{
4+
"name": "default",
5+
"value": "88adV34UZhaydCLOjMzbMgUtXh3stBnL8LFcL9R17DL8HAY8PVhpZA==",
6+
"encrypted": false
7+
}
8+
]
9+
}

src/WebJobs.Script/Binding/Http/HttpTriggerAttributeBindingProvider.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ public Task<ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext contex
3737
return Task.FromResult<ITriggerBinding>(null);
3838
}
3939

40-
// Can bind to user types, HttpRequestMessage and all the Read
40+
// Can bind to user types, HttpRequestMessage, object (for dynamic binding support) and all the Read
4141
// Types supported by StreamValueBinder
4242
IEnumerable<Type> supportedTypes = StreamValueBinder.GetSupportedTypes(FileAccess.Read)
43-
.Union(new Type[] { typeof(HttpRequestMessage) });
43+
.Union(new Type[] { typeof(HttpRequestMessage), typeof(object) });
4444
bool isSupportedTypeBinding = ValueBinder.MatchParameterType(parameter, supportedTypes);
4545
bool isUserTypeBinding = !isSupportedTypeBinding && Utility.IsValidUserType(parameter.ParameterType);
4646
if (!isSupportedTypeBinding && !isUserTypeBinding)
@@ -313,6 +313,12 @@ public override object GetValue()
313313
{
314314
return _request;
315315
}
316+
else if (_parameter.ParameterType == typeof(object))
317+
{
318+
// for dynamic, we read as an object, which will actually return
319+
// a JObject which is dynamic
320+
return _request.Content.ReadAsAsync<object>().GetAwaiter().GetResult();
321+
}
316322

317323
return base.GetValue();
318324
}

src/WebJobs.Script/Binding/HttpBinding.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
using System.Reflection.Emit;
1414
using System.Threading.Tasks;
1515
using System.Web.Http;
16-
using System.Web.Http.Results;
1716
using Microsoft.Azure.WebJobs.Script.Description;
1817
using Newtonsoft.Json;
1918
using Newtonsoft.Json.Linq;

src/WebJobs.Script/Description/DotNet/DotNetFunctionDescriptorProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ protected override Collection<ParameterDescriptor> GetFunctionParameters(IFuncti
157157
}
158158

159159
// If we have an HTTP trigger binding but no parameter binds to the raw HttpRequestMessage,
160-
// add it as a system parameter
160+
// add it as a system parameter so it is accessible later in the pipeline.
161161
if (string.Compare(triggerMetadata.Type, "httptrigger", StringComparison.OrdinalIgnoreCase) == 0 &&
162162
!descriptors.Any(p => p.Type == typeof(HttpRequestMessage)))
163163
{

test/WebJobs.Script.Tests/CSharpEndToEndTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.Linq;
8+
using System.Net;
89
using System.Net.Http;
10+
using System.Net.Http.Headers;
911
using System.Threading.Tasks;
12+
using System.Web.Http;
1013
using Microsoft.Azure.WebJobs.Script.Tests.ApiHub;
1114
using Microsoft.CodeAnalysis;
1215
using Microsoft.CodeAnalysis.CSharp;
@@ -248,6 +251,38 @@ public async Task Scenario_RandGuidBinding_GeneratesRandomIDs()
248251
}
249252
}
250253

254+
[Fact]
255+
public async Task HttpTrigger_Post_Dynamic()
256+
{
257+
var input = new JObject
258+
{
259+
{ "name", "Mathew Charles" },
260+
{ "location", "Seattle" }
261+
};
262+
263+
HttpRequestMessage request = new HttpRequestMessage
264+
{
265+
RequestUri = new Uri(string.Format("http://localhost/api/httptrigger-dynamic")),
266+
Method = HttpMethod.Post,
267+
Content = new StringContent(input.ToString())
268+
};
269+
request.SetConfiguration(new HttpConfiguration());
270+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
271+
272+
Dictionary<string, object> arguments = new Dictionary<string, object>
273+
{
274+
{ "input", request },
275+
{ ScriptConstants.SystemTriggerParameterName, request }
276+
};
277+
await Fixture.Host.CallAsync("HttpTrigger-Dynamic", arguments);
278+
279+
HttpResponseMessage response = (HttpResponseMessage)request.Properties[ScriptConstants.AzureFunctionsHttpResponseKey];
280+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
281+
282+
string body = await response.Content.ReadAsStringAsync();
283+
Assert.Equal("Name: Mathew Charles, Location: Seattle", body);
284+
}
285+
251286
public class TestFixture : EndToEndTestFixture
252287
{
253288
private const string ScriptRoot = @"TestScripts\CSharp";

test/WebJobs.Script.Tests/SamplesEndToEndTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,21 @@ public async Task GenericWebHook_CSharp_Post_Succeeds()
468468
Assert.Equal("Value: Foobar Action: test", jsonObject["result"]);
469469
}
470470

471+
[Fact]
472+
public async Task GenericWebHook_CSharp_Dynamic_Post_Succeeds()
473+
{
474+
string uri = "api/webhook-generic-csharp-dynamic?code=88adV34UZhaydCLOjMzbMgUtXh3stBnL8LFcL9R17DL8HAY8PVhpZA==";
475+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
476+
request.Content = new StringContent("{ 'Value': 'Foobar' }");
477+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
478+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
479+
480+
HttpResponseMessage response = await this._fixture.HttpClient.SendAsync(request);
481+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
482+
string body = await response.Content.ReadAsStringAsync();
483+
Assert.Equal("\"Value: Foobar\"", body);
484+
}
485+
471486
[Fact]
472487
public async Task AzureWebHook_CSharp_Post_Succeeds()
473488
{
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"bindings": [
3+
{
4+
"type": "httpTrigger",
5+
"name": "input",
6+
"direction": "in",
7+
"methods": [ "post" ]
8+
},
9+
{
10+
"type": "http",
11+
"name": "$return",
12+
"direction": "out"
13+
}
14+
]
15+
}

0 commit comments

Comments
 (0)