Skip to content

Commit 0286446

Browse files
committed
Enable passing return values from non-C# functions.
* Annotate that a binding handles a return value via new HandlesReturnAttribute in azure-webjobs-sdk.
1 parent 4fe222b commit 0286446

File tree

5 files changed

+71
-45
lines changed

5 files changed

+71
-45
lines changed

src/WebJobs.Script/Binding/ExtensionBinding.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public ExtensionBinding(ScriptHostConfiguration config, ScriptBinding binding, B
2828
Attributes = _binding.GetAttributes();
2929
}
3030

31-
internal Collection<Attribute> Attributes { get; private set; }
31+
public Collection<Attribute> Attributes { get; private set; }
3232

3333
public override Collection<CustomAttributeBuilder> GetCustomAttributes(Type parameterType)
3434
{

src/WebJobs.Script/Binding/FunctionBinding.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ internal static Collection<FunctionBinding> GetBindings(ScriptHostConfiguration
5353
{
5454
throw new InvalidOperationException("Http binding can only be used for output.");
5555
}
56-
bindings.Add(new HttpBinding(config, bindingMetadata, FileAccess.Write));
5756
break;
5857
default:
5958
FunctionBinding binding = null;

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

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,21 @@
33

44
using System;
55
using System.Collections.Generic;
6-
using System.Collections.ObjectModel;
76
using System.Dynamic;
87
using System.Globalization;
98
using System.IO;
10-
using System.Linq;
119
using System.Net;
12-
using System.Reflection.Emit;
13-
using System.Threading.Tasks;
1410
using Microsoft.AspNetCore.Http;
1511
using Microsoft.AspNetCore.Mvc;
1612
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
1713
using Microsoft.Azure.WebJobs.Script.Description;
18-
using Microsoft.Net.Http.Headers;
1914
using Newtonsoft.Json;
2015
using Newtonsoft.Json.Linq;
2116

2217
namespace Microsoft.Azure.WebJobs.Script.Binding
2318
{
24-
public class HttpBinding : FunctionBinding
19+
public class HttpBinding
2520
{
26-
public HttpBinding(ScriptHostConfiguration config, BindingMetadata metadata, FileAccess access)
27-
: base(config, metadata, access)
28-
{
29-
}
30-
31-
public override Collection<CustomAttributeBuilder> GetCustomAttributes(Type parameterType)
32-
{
33-
return null;
34-
}
35-
36-
public override Task BindAsync(BindingContext context)
37-
{
38-
HttpRequest request = (HttpRequest)context.TriggerValue;
39-
40-
object content = context.Value;
41-
if (content is Stream)
42-
{
43-
// for script language functions (e.g. PowerShell, BAT, etc.) the value
44-
// will be a Stream which we need to convert to string
45-
ConvertStreamToValue((Stream)content, DataType.String, ref content);
46-
}
47-
48-
IActionResult response = CreateResult(request, content);
49-
request.HttpContext.Items[ScriptConstants.AzureFunctionsHttpResponseKey] = response;
50-
51-
return Task.CompletedTask;
52-
}
53-
5421
internal static IActionResult CreateResult(HttpRequest request, object content)
5522
{
5623
string stringContent = content as string;
@@ -188,17 +155,31 @@ internal static void SetResponse(HttpRequest request, object result)
188155
IActionResult actionResult = result as IActionResult;
189156
if (actionResult == null)
190157
{
191-
var objectResult = new ObjectResult(result);
192-
193-
if (result is System.Net.Http.HttpResponseMessage)
158+
if (result is Stream)
194159
{
195-
// To maintain backwards compatibility, if the type returned is an
196-
// instance of an HttpResponseMessage, add the appropriate formatter to
197-
// handle the response
198-
objectResult.Formatters.Add(new HttpResponseMessageOutputFormatter());
160+
// for script language functions (e.g. PowerShell, BAT, etc.) the value
161+
// will be a Stream which we need to convert to string
162+
FunctionBinding.ConvertStreamToValue((Stream)result, DataType.String, ref result);
163+
actionResult = CreateResult(request, result);
199164
}
165+
else if (result is JObject)
166+
{
167+
actionResult = CreateResult(request, result);
168+
}
169+
else
170+
{
171+
var objectResult = new ObjectResult(result);
200172

201-
actionResult = objectResult;
173+
if (result is System.Net.Http.HttpResponseMessage)
174+
{
175+
// To maintain backwards compatibility, if the type returned is an
176+
// instance of an HttpResponseMessage, add the appropriate formatter to
177+
// handle the response
178+
objectResult.Formatters.Add(new HttpResponseMessageOutputFormatter());
179+
}
180+
181+
actionResult = objectResult;
182+
}
202183
}
203184

204185
request.HttpContext.Items[ScriptConstants.AzureFunctionsHttpResponseKey] = actionResult;

src/WebJobs.Script/Description/Rpc/WorkerFunctionDescriptorProvider.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
using System;
55
using System.Collections.ObjectModel;
6+
using System.Linq;
7+
using System.Reflection;
8+
using System.Reflection.Emit;
69
using System.Threading.Tasks.Dataflow;
10+
using Microsoft.Azure.WebJobs.Description;
711
using Microsoft.Azure.WebJobs.Script.Binding;
812
using Microsoft.Azure.WebJobs.Script.Rpc;
913

@@ -40,5 +44,47 @@ protected override IFunctionInvoker CreateFunctionInvoker(string scriptFilePath,
4044
});
4145
return new WorkerLanguageInvoker(Host, triggerMetadata, functionMetadata, inputBindings, outputBindings, inputBuffer);
4246
}
47+
48+
protected override Collection<ParameterDescriptor> GetFunctionParameters(IFunctionInvoker functionInvoker, FunctionMetadata functionMetadata,
49+
BindingMetadata triggerMetadata, Collection<CustomAttributeBuilder> methodAttributes, Collection<FunctionBinding> inputBindings, Collection<FunctionBinding> outputBindings)
50+
{
51+
var parameters = base.GetFunctionParameters(functionInvoker, functionMetadata, triggerMetadata, methodAttributes, inputBindings, outputBindings);
52+
53+
var bindings = inputBindings.Union(outputBindings);
54+
55+
try
56+
{
57+
var triggerHandlesReturnValueBinding = bindings.SingleOrDefault(b =>
58+
b.Metadata.IsTrigger &&
59+
(b as ExtensionBinding)?.Attributes.SingleOrDefault(a =>
60+
(a.GetType().GetCustomAttribute(typeof(BindingAttribute)) as BindingAttribute)?.TriggerHandlesReturnValue == true)
61+
!= null);
62+
63+
if (triggerHandlesReturnValueBinding != null)
64+
{
65+
var byRefType = typeof(object).MakeByRefType();
66+
67+
ParameterDescriptor returnDescriptor = new ParameterDescriptor(ScriptConstants.SystemReturnParameterName, byRefType);
68+
returnDescriptor.Attributes |= ParameterAttributes.Out;
69+
70+
Collection<CustomAttributeBuilder> customAttributes = triggerHandlesReturnValueBinding.GetCustomAttributes(byRefType);
71+
if (customAttributes != null)
72+
{
73+
foreach (var customAttribute in customAttributes)
74+
{
75+
returnDescriptor.CustomAttributes.Add(customAttribute);
76+
}
77+
}
78+
79+
parameters.Add(returnDescriptor);
80+
}
81+
}
82+
catch (InvalidOperationException ex)
83+
{
84+
throw new InvalidOperationException("Multiple bindings cannot be designated as HandlesReturnValue.", ex);
85+
}
86+
87+
return parameters;
88+
}
4389
}
4490
}

src/WebJobs.Script/Description/Rpc/WorkerLanguageInvoker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protected override async Task<object> InvokeCore(object[] parameters, FunctionIn
7373
result = await invocationContext.ResultSource.Task;
7474

7575
await BindOutputsAsync(triggerValue, context.Binder, result);
76-
return null;
76+
return result.Return;
7777
}
7878

7979
private async Task<(string name, DataType type, object value)[]> BindInputsAsync(Binder binder)

0 commit comments

Comments
 (0)