Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Isolated Http Trigger [FromQuery] bool parameterName cannot serialize Pascal case True or False #2786

Open
vtnzDirkMomsen opened this issue Oct 17, 2024 · 1 comment
Labels
area: http Items related to experience improvements for HTTP triggers enhancement New feature or request

Comments

@vtnzDirkMomsen
Copy link

vtnzDirkMomsen commented Oct 17, 2024

Repro steps

  1. Create a function as follows on a default .net 8 isolated function app:
public class Function1
{
    public Function1()
    {
    }

    [Function("Function1")]
    public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req,
        [FromQuery] bool includeSomething = false)
    {
        return new OkObjectResult($"Did you include something? {includeSomething}");
    }
}
  1. Run function app
  2. Send get request to function endpoint: http://localhost:7107/api/Function1?includeSomething=False

Expected behavior

Expected response body: Did you include something? False

Actual behavior

Http Error - 500 Internal Server Error occurs
Function app console logs are as follows:

[2024-10-17T09:17:56.773Z] Function 'Function1', Invocation id '07a9025c-876f-4f43-a6a1-2421edad600c': An exception was thrown by the invocation.
[2024-10-17T09:17:56.775Z] Result: Function 'Function1', Invocation id '07a9025c-876f-4f43-a6a1-2421edad600c': An exception was thrown by the invocation.
Exception: Microsoft.Azure.Functions.Worker.FunctionInputConverterException: Error converting 1 input parameters for Function 'Function1': Cannot convert input parameter 'includeSomething' to type 'System.Boolean' from type 'System.String'. Error:System.Text.Json.JsonException: 'F' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
[2024-10-17T09:17:56.777Z]  ---> System.Text.Json.JsonReaderException: 'F' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
[2024-10-17T09:17:56.780Z]    at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
[2024-10-17T09:17:56.783Z]    at System.Text.Json.Utf8JsonReader.ConsumeValue(Byte marker)
[2024-10-17T09:17:56.784Z]    at System.Text.Json.Utf8JsonReader.ReadFirstToken(Byte first)
[2024-10-17T09:17:56.786Z]    at System.Text.Json.Utf8JsonReader.ReadSingleSegment()
[2024-10-17T09:17:56.799Z]    at System.Text.Json.Utf8JsonReader.Read()
[2024-10-17T09:17:56.800Z]    at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
[2024-10-17T09:17:56.802Z]    --- End of inner exception stack trace ---
[2024-10-17T09:17:56.805Z]    at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, JsonReaderException ex)
[2024-10-17T09:17:56.807Z]    at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
[2024-10-17T09:17:56.813Z]    at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
[2024-10-17T09:17:56.817Z]    at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
[2024-10-17T09:17:56.820Z]    at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
[2024-10-17T09:17:56.822Z]    at Microsoft.Azure.Functions.Worker.Converters.JsonPocoConverter.GetConversionResultFromDeserialization(Byte[] bytes, Type type) in D:\a\_work\1\s\src\DotNetWorker.Core\Converters\JsonPocoConverter.cs:line 66
[2024-10-17T09:17:56.824Z]    at Microsoft.Azure.Functions.Worker.Context.Features.DefaultFunctionInputBindingFeature.BindFunctionInputAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\Context\Features\DefaultFunctionInputBindingFeature.cs:line 97
[2024-10-17T09:17:56.828Z]    at FromQueryTest.DirectFunctionExecutor.ExecuteAsync(FunctionContext context) in C:\Users\momsend\source\repos\FromQueryTest\FromQueryTest\obj\Debug\net8.0\Microsoft.Azure.Functions.Worker.Sdk.Generators\Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionExecutorGenerator\GeneratedFunctionExecutor.g.cs:line 31
[2024-10-17T09:17:56.831Z]    at Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs:line 13
[2024-10-17T09:17:56.834Z]    at Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.FunctionsHttpProxyingMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in /mnt/vss/_work/1/s/extensions/Worker.Extensions.Http.AspNetCore/src/FunctionsMiddleware/FunctionsHttpProxyingMiddleware.cs:line 54
[2024-10-17T09:17:56.835Z]    at Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\FunctionsApplication.cs:line 91
Stack:    at Microsoft.Azure.Functions.Worker.Context.Features.DefaultFunctionInputBindingFeature.BindFunctionInputAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\Context\Features\DefaultFunctionInputBindingFeature.cs:line 97
[2024-10-17T09:17:56.837Z]    at FromQueryTest.DirectFunctionExecutor.ExecuteAsync(FunctionContext context) in C:\Users\momsend\source\repos\FromQueryTest\FromQueryTest\obj\Debug\net8.0\Microsoft.Azure.Functions.Worker.Sdk.Generators\Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionExecutorGenerator\GeneratedFunctionExecutor.g.cs:line 31
[2024-10-17T09:17:56.840Z]    at Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs:line 13
[2024-10-17T09:17:56.843Z]    at Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.FunctionsHttpProxyingMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in /mnt/vss/_work/1/s/extensions/Worker.Extensions.Http.AspNetCore/src/FunctionsMiddleware/FunctionsHttpProxyingMiddleware.cs:line 54
[2024-10-17T09:17:56.845Z]    at Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\FunctionsApplication.cs:line 91.
[2024-10-17T09:17:56.862Z] Executed 'Functions.Function1' (Failed, Id=07a9025c-876f-4f43-a6a1-2421edad600c, Duration=217ms)
[2024-10-17T09:17:56.864Z] System.Private.CoreLib: Exception while executing function: Functions.Function1. System.Private.CoreLib: Result: Failure
Exception: Microsoft.Azure.Functions.Worker.FunctionInputConverterException: Error converting 1 input parameters for Function 'Function1': Cannot convert input parameter 'includeSomething' to type 'System.Boolean' from type 'System.String'. Error:System.Text.Json.JsonException: 'F' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
[2024-10-17T09:17:56.866Z]  ---> System.Text.Json.JsonReaderException: 'F' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
[2024-10-17T09:17:56.868Z]    at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
[2024-10-17T09:17:56.870Z]    at System.Text.Json.Utf8JsonReader.ConsumeValue(Byte marker)
[2024-10-17T09:17:56.873Z]    at System.Text.Json.Utf8JsonReader.ReadFirstToken(Byte first)
[2024-10-17T09:17:56.875Z]    at System.Text.Json.Utf8JsonReader.ReadSingleSegment()
[2024-10-17T09:17:56.877Z]    at System.Text.Json.Utf8JsonReader.Read()
[2024-10-17T09:17:56.878Z]    at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
[2024-10-17T09:17:56.880Z]    --- End of inner exception stack trace ---
[2024-10-17T09:17:56.882Z]    at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, JsonReaderException ex)
[2024-10-17T09:17:56.883Z]    at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
[2024-10-17T09:17:56.886Z]    at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
[2024-10-17T09:17:56.889Z]    at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
[2024-10-17T09:17:56.890Z]    at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
[2024-10-17T09:17:56.890Z]    at Microsoft.Azure.Functions.Worker.Converters.JsonPocoConverter.GetConversionResultFromDeserialization(Byte[] bytes, Type type) in D:\a\_work\1\s\src\DotNetWorker.Core\Converters\JsonPocoConverter.cs:line 66
[2024-10-17T09:17:56.891Z]    at Microsoft.Azure.Functions.Worker.Context.Features.DefaultFunctionInputBindingFeature.BindFunctionInputAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\Context\Features\DefaultFunctionInputBindingFeature.cs:line 97
[2024-10-17T09:17:56.892Z]    at FromQueryTest.DirectFunctionExecutor.ExecuteAsync(FunctionContext context) in C:\Users\momsend\source\repos\FromQueryTest\FromQueryTest\obj\Debug\net8.0\Microsoft.Azure.Functions.Worker.Sdk.Generators\Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionExecutorGenerator\GeneratedFunctionExecutor.g.cs:line 31
[2024-10-17T09:17:56.893Z]    at Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs:line 13
[2024-10-17T09:17:56.894Z]    at Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.FunctionsHttpProxyingMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in /mnt/vss/_work/1/s/extensions/Worker.Extensions.Http.AspNetCore/src/FunctionsMiddleware/FunctionsHttpProxyingMiddleware.cs:line 54
[2024-10-17T09:17:56.895Z]    at Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\FunctionsApplication.cs:line 91
[2024-10-17T09:17:56.897Z]    at Microsoft.Azure.Functions.Worker.Handlers.InvocationHandler.InvokeAsync(InvocationRequest request) in D:\a\_work\1\s\src\DotNetWorker.Grpc\Handlers\InvocationHandler.cs:line 88
Stack:    at Microsoft.Azure.Functions.Worker.Context.Features.DefaultFunctionInputBindingFeature.BindFunctionInputAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\Context\Features\DefaultFunctionInputBindingFeature.cs:line 97
[2024-10-17T09:17:56.898Z]    at FromQueryTest.DirectFunctionExecutor.ExecuteAsync(FunctionContext context) in C:\Users\momsend\source\repos\FromQueryTest\FromQueryTest\obj\Debug\net8.0\Microsoft.Azure.Functions.Worker.Sdk.Generators\Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionExecutorGenerator\GeneratedFunctionExecutor.g.cs:line 31
[2024-10-17T09:17:56.899Z]    at Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs:line 13
[2024-10-17T09:17:56.900Z]    at Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.FunctionsHttpProxyingMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in /mnt/vss/_work/1/s/extensions/Worker.Extensions.Http.AspNetCore/src/FunctionsMiddleware/FunctionsHttpProxyingMiddleware.cs:line 54
[2024-10-17T09:17:56.901Z]    at Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\FunctionsApplication.cs:line 91
[2024-10-17T09:17:56.902Z]    at Microsoft.Azure.Functions.Worker.Handlers.InvocationHandler.InvokeAsync(InvocationRequest request) in D:\a\_work\1\s\src\DotNetWorker.Grpc\Handlers\InvocationHandler.cs:line 88.

Known workarounds

The obvious workaround is the use lowercase first letter true or false e.g.
http://localhost:7107/api/Function1?includeSomething=false
This will work.

To support pascal case we can use a string input parameter and parse it:

[Function("StringInputParam")]
public IActionResult StringInputParam([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req,
    [FromQuery] string includeSomething)
{
    bool convertedToBool = bool.Parse(includeSomething);

    return new OkObjectResult($"Function2 Did you include something? {convertedToBool}");
}

Related information

The issue is the same whether you run the application locally or deployed in azure.

Can be replicated on a default .net isolated azure function app

Program.cs:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services =>
    {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
    })
    .Build();

host.Run();

host.json:

{
    "version": "2.0",
    "logging": {
        "applicationInsights": {
            "samplingSettings": {
                "isEnabled": true,
                "excludedTypes": "Request"
            },
            "enableLiveMetricsFilters": true
        }
    }
}

local.settings.json:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
    }
}

Azure Logic Apps that use the Http Swagger action or the API Management action will automatically provide a "False" or "True" to query parameters coded as boolean, so I have to support both camel and pascal case when migrating to the isolated worker.

This is mostly just frustrating but there are simple ways to work around it.

@cjaliaga cjaliaga transferred this issue from Azure/azure-functions-host Oct 17, 2024
@vtnzDirkMomsen
Copy link
Author

It should also be noted that when I add services.AddMvc().AddNewtonsoftJson(); to the HostBuilder.ConfigureServices (in Program.cs) nothing changes. It still seems to use System.Text.Json

@satvu satvu added area: http Items related to experience improvements for HTTP triggers enhancement New feature or request and removed Needs: Triage (Functions) labels Oct 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: http Items related to experience improvements for HTTP triggers enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants