Azure / Azure/azure-functions-dotnet-worker

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

Open
#2,786 1 comment 0 reactions 0 assignees View on GitHub
area: http enhancement
Dominant language
C#
Stars
466
Forks
215
Avg merge
3d 10h
Merged PRs (30d)
7

Description

#### 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}");
}
}
```
2. Run function app
3. 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.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by tracing query-parameter conversion through src/DotNetWorker.Core/Converters/JsonPocoConverter.cs and src/DotNetWorker.Core/Context/Features/DefaultFunctionInputBindingFeature.cs, using the supplied isolated .NET 8 reproduction as the failing case. Identify the existing input-conversion tests, then verify that Pascal-case query values True and False are accepted for bool parameters without breaking lowercase values or other conversions.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, csharp
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.