azure-webjobs-sdk-extensions
azure-webjobs-sdk-extensions copied to clipboard
HttpTrigger binding to POCO fails on Enums
I've recently updated my Azure Functions app to v2.0.12050.0 which upgrade the webjobs-sdk-extensions from 3.0.0-beta5 to 3.0.0-beta8.
I have several functions using HttpTrigger in POST binding to a POCO object. The POCO has an enum property and before the upgrade I was able to post content into it and the sdk was correctly binding an int value to the enum. See below for more details.
POCO:
public class Message
{
public Region RegionId { get; set; }
public string Content { get; set; }
}
public enum Region
{
UK = 1
}
Request payload:
{
RegionId: 1,
Content: "test"
}
Function declaration:
public static async Task ProcessMessage([HttpTrigger(AuthorizationLevel.Anonymous, "POST")]Message message);
Expected behavior
The message property should be populated from the payload and the enum property Region correctly set.
Actual behavior
Invocation breaks with the error:
System.Private.CoreLib: Exception while executing function: AddRequestTweakingWebsiteRule. Microsoft.Azure.WebJobs.Host: Exception binding parameter 'req'. System.Private.CoreLib: Invalid cast from 'System.String' to 'LoveTheSales.Shared.Enums.Region'.
Stacktrace:
System.InvalidOperationException: Exception binding parameter 'req' ---> System.InvalidCastException: Invalid cast from 'System.String' to 'Shared.Enums.Region'.
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
at System.String.System.IConvertible.ToType(Type type, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType)
at Microsoft.Azure.WebJobs.Extensions.Http.HttpTriggerAttributeBindingProvider.HttpTriggerBinding.ConvertValueIfNecessary(Object value, Type targetType) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions.Http\HttpTriggerAttributeBindingProvider.cs:line 413
at Microsoft.Azure.WebJobs.Extensions.Http.HttpTriggerAttributeBindingProvider.HttpTriggerBinding.ApplyBindingData(Object target, IDictionary`2 bindingData) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions.Http\HttpTriggerAttributeBindingProvider.cs:line 228
at Microsoft.Azure.WebJobs.Extensions.Http.HttpTriggerAttributeBindingProvider.HttpTriggerBinding.BindAsync(Object value, ValueBindingContext context) in C:\azure-webjobs-sdk-extensions\src\WebJobs.Extensions.Http\HttpTriggerAttributeBindingProvider.cs:line 174
at Microsoft.Azure.WebJobs.Host.Triggers.TriggeredFunctionBinding`1.BindCoreAsync(ValueBindingContext context, Object value, IDictionary`2 parameters) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Triggers\TriggeredFunctionBinding.cs:line 57
--- End of inner exception stack trace ---
at Microsoft.Azure.WebJobs.Host.Executors.DelayedException.Throw() in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\DelayedException.cs:line 27
at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithWatchersAsync(IFunctionInstance instance, ParameterHelper parameterHelper, ILogger logger, CancellationTokenSource functionCancellationTokenSource) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 478
at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(IFunctionInstance instance, ParameterHelper parameterHelper, IFunctionOutputDefinition outputDefinition, ILogger logger, CancellationTokenSource functionCancellationTokenSource) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 444
at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(IFunctionInstance instance, FunctionStartedMessage message, FunctionInstanceLogEntry instanceLogEntry, ParameterHelper parameterHelper, ILogger logger, CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 249
--- End of inner exception stack trace ---
at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(IFunctionInstance instance, FunctionStartedMessage message, FunctionInstanceLogEntry instanceLogEntry, ParameterHelper parameterHelper, ILogger logger, CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 293
Known workarounds
I can change the HttpTrigger to bind to HttpRequestMessage and deserialize the content manually into my POCO but I was working fine before the upgrade.
Related information
This started to happen when we upgraded from 3.0.0-beta5 to 3.0.0-beta8.
Seems guys messed things a lil bit in 3.0.0-beta8 :) https://github.com/Azure/azure-webjobs-sdk-extensions/issues/485
Having the same issue. Is this possible to solve in the startup configurations? Or do I have to deserialize it within the azure function?
System.Private.CoreLib: Exception while executing function: PostSomething.
Microsoft.Azure.WebJobs.Host: One or more errors occurred. (Exception binding parameter 'something') (Exception binding parameter 'req').
Exception binding parameter 'something'. System.Private.CoreLib:
Invalid cast from 'System.String' to 'My.SomethingEnum'.
This is still an issue in the release version 3.0.0
and is a real big showstopper for our project. @mathewc or @brettsam, could you please comment on this issue?
I see this is a P1 issue now. Any updates on this? We had a clean api which is now cluttered with workarounds to address this.
I will take a shot at this in this Sprint.
We looked at this, and it's an issue with our config for serialization not being propagated. It will need some design changes (possibly in the next sprint). In the meanwhile, @fabiocav, would you mind sharing the work-around here?
As @ankitkumarr mentioned, this is a configuration setup issue, but the goo news is that there's a clear workaround. While the problem is not resolved, please add the following to your function project (in addition to a reference to Microsoft.Azure.Functions.Extensions
:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
[assembly: FunctionsStartup(typeof(MyFunctionApp.FunctionAppStartup))]
namespace MyFunctionApp
{
public class FunctionAppStartup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddTransient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>();
}
}
}
Just adding comment here - still a valid issue but fixing this outright would be a breaking change as it would impact the default serialization logic. We have some thoughts on how we may be able to make this easier, but for now following the recommendation from @fabiocav above and adding some MvcJsonMvcOptions in the DI pipeline is best way to get the expected POCO serialization. Removing P1 label, but still tracking and leaving open, so feel free to keep feedback / comments / gaps on this issue.
To follow on from @fabiocav's comment above, that works perfectly but you may also need a reference to Microsoft.AspNetCore.Mvc.Formatters.Json
.
Even with the work around, I can't get my function to run, I currently have a model that holds a list of strings. i have a second input binding, when the second input binding try to access the {Ids} it's empty.
It seems like the dictionary holding the values is not getting populate, even with the work around. If I access the RequestQuery object within the function it's correctly populated.
Changing the RequestQuery class to a hold string instead of a list, it works fine.
is there any timeline for when this is getting fixed @jeffhollan ???
[FunctionName("SelectCustomerSample6")]
public IActionResult SelectCustomerSample6(
[HttpTrigger()] RequestQuery requestQuery,
[Dapper(Sql = "Select6.sql", SqlConnection = "SqlConnection",
Parameters = "Ids:{Ids}")] IList<Customer> customers,
ILogger log)
{
return new OkObjectResult(customers);
}
public class RequestQuery
{
public List<string> Ids{ get; set; }
}
To follow on from @fabiocav's comment above, that works perfectly but you may also need a reference to
Microsoft.AspNetCore.Mvc.Formatters.Json
.
doesn't exist for core 3.1?
@fabiocav I can't get the work-around to run on Azure Functions V2:
[11-02-2020 12:50:13] A host error has occurred during startup operation 'e5061744-cc42-4f93-991a-71a3a52eb7e0'.
[11-02-2020 12:50:13] Hemonto.Asset: Could not load type 'Microsoft.AspNetCore.Mvc.Formatters.Json.Internal.MvcJsonMvcOptio
nsSetup' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb97938
29ddae60'.
I tried adding a reference to NuGet package Microsoft.AspNetCore.Mvc.Formatters.Json
(ver. 2.2.0) but still no luck.
Using:
Azure Functions Core Tools (3.0.2009 Commit hash: 77395527a4e9c28da8400dcfd1a450f4e0d0c36c)
Function Runtime Version: 3.0.12930.0
@CasperWSchmidt I'm assuming you mean Functions V3 above, correct? Have you tried to move to 3.0 and remove the workaround?
@fabiocav no I mean V2 as in. Net core 2.2. My colleague tried migrating one of our services to V3 but it didn't work out-of-the-box and we're on a tight schedule so we won't be able to try that right now
@fabiocav @CasperWSchmidt I get the same error on Functions V3 and net core 3.1:
Could not load type 'Microsoft.AspNetCore.Mvc.Formatters.Json.Internal.MvcJsonMvcOptionsSetup' from assembly 'Microsoft.AspNetCore.Mv
c.Formatters.Json, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
I'm getting the same error as @maxbog.
Could not load type 'Microsoft.AspNetCore.Mvc.Formatters.Json.Internal.MvcJsonMvcOptionsSetup' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'
The problem persists?
@fabioleardini this is an old issue and I want to make sure what's described here applies to you.
Would you be able to share your setup details and, ideally, a link to a repro so we can look at how you have this setup? Also, which of the issues described above are you experiencing? The originally described problem, or the load issue with the workaround?
Thanks!
@fabiocav I have done some quick testing and I can confirm that on AZ V3 there are some issues binding querystring parameters to enum and lists (IEnumerable<string>
) (using a POST request to test also combination of querystring and body). It works just fine posting it all in the body. Should I create a new issue for this?
FunctionAppHttpParameterTesting.zip
Here's a small example. I have attached three requests in a Postman collection as well. Enabling and disabling the querystring parameters that maps to enum
or IEnumerable<string>
will either result in 500 - Internal server error (if any is enabled) or 200 - Ok with the rest of the request serialized as the output.
@fabiocav any news here?
Still nothing? I'm amazed that a bug/severe limitation like this get so little attention :(
@CasperWSchmidt did you find any workaround for this? I'm also facing the same issue with enum. @fabiocav can we expect any help on this please?
After adding the line what @ankitkumarr mentioned, getting following error -
System.TypeLoadException: 'Could not load type 'Microsoft.AspNetCore.Mvc.Formatters.Json.Internal.MvcJsonMvcOptionsSetup' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.1.14.0
@rahul230691 the only workaround I know of is to do the deserialization yourself using Newtonsoft/Json.Net but this is not easy if you have values in both querystring and body.
@fabiocav @jeffhollan could you at least give some kind of response?
It appears to also be a problem for net6.0 on Functions V4 (referencing Microsoft.NET.Sdk.Functions 4.1.1) @fabiocav @jeffhollan
Any plans on resolving this? Still an issue for .net6.0 and Microsoft.Net.Sdk.Functions 4.0.1
Seems like lots of people are having this issue and are forced to manually bind the models with enums that comes from querystring.
@BullFrog13 In my experience this repo is DEAD. I guess all their focus is on the isolated model (the only model going forward now) so this regression (from V2) is just ignored 👎 Also from what I can see on Twitter, @jeffhollan is no longer part of the Azure Functions team. @fabiocav should be part of it still though...