Azure / Azure/azure-functions-dotnet-worker
DateOnly and TimeOnly parameter binding throws exception
- Dominant language
- C#
- Stars
- 466
- Forks
- 215
- Avg merge
- 3d 10h
- Merged PRs (30d)
- 7
Description
### Description
When using a DateOnly or TimeOnly parameter binding in a function (for example when extracting from the route of a HTTP Trigger or as a parameter in a SignalR binding), the function invocator throws the following exception respectively.
DateOnly
```
Microsoft.Azure.Functions.Worker.FunctionInputConverterException: Error converting 1 input parameters for Function 'DateOnlyTest': Cannot convert input parameter 'date' to type 'System.DateOnly' from type 'System.String'. Error:System.Text.Json.JsonException: '-' is an invalid end of a number. Expected a delimiter. Path: $ | LineNumber: 0 | BytePositionInLine: 4.
---> System.Text.Json.JsonReaderException: '-' is an invalid end of a number. Expected a delimiter. LineNumber: 0 | BytePositionInLine: 4.
at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
at System.Text.Json.Utf8JsonReader.TryGetNumber(ReadOnlySpan`1 data, Int32& consumed)
at System.Text.Json.Utf8JsonReader.ReadFirstToken(Byte first)
at System.Text.Json.Utf8JsonReader.ReadSingleSegment()
at System.Text.Json.Utf8JsonReader.Read()
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, JsonReaderException ex)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
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
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
at SignalRSandbox.Functions.DirectFunctionExecutor.ExecuteAsync(FunctionContext context)
```
TimeOnly
```
Microsoft.Azure.Functions.Worker.FunctionInputConverterException: Error converting 1 input parameters for Function 'TimeOnlyTest': Cannot convert input parameter 'time' to type 'System.TimeOnly' from type 'System.String'. Error:System.Text.Json.JsonException: Invalid leading zero before '9'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
---> System.Text.Json.JsonReaderException: Invalid leading zero before '9'. LineNumber: 0 | BytePositionInLine: 1.
at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
at System.Text.Json.Utf8JsonReader.ConsumeZero(ReadOnlySpan`1& data, Int32& i)
at System.Text.Json.Utf8JsonReader.TryGetNumber(ReadOnlySpan`1 data, Int32& consumed)
at System.Text.Json.Utf8JsonReader.ReadFirstToken(Byte first)
at System.Text.Json.Utf8JsonReader.ReadSingleSegment()
at System.Text.Json.Utf8JsonReader.Read()
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, JsonReaderException ex)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
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
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
at SignalRSandbox.Functions.DirectFunctionExecutor.ExecuteAsync(FunctionContext context)
```
Things of note:
I'm using the Asp.NET Core integration for this test.
The parameter binding however works for a DateTime parameter, as can be seen in the sample code below.
Hereby also the list of used libraries (all are latest version at the time of writing)
```xml
```
### Steps to reproduce
Test functions being used.
```csharp
[Function("DateTimeTest")]
public HttpResponseData DateTimeTest(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "datetime/{dateTime}")] HttpRequestData req,
DateTime dateTime)
{
_logger.LogInformation("Received datetime: {DateTime}", dateTime);
return req.CreateResponse(HttpStatusCode.NoContent);
}
[Function("DateOnlyTest")]
public HttpResponseData DateOnlyTest(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "dateonly/{date}")] HttpRequestData req,
DateOnly date)
{
_logger.LogInformation("Received date: {Date}", date);
return req.CreateResponse(HttpStatusCode.NoContent);
}
[Function("TimeOnlyTest")]
public HttpResponseData TimeOnlyTest(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "timeonly/{time}")] HttpRequestData req,
TimeOnly time)
{
_logger.LogInformation("Received time: {Time}", time);
return req.CreateResponse(HttpStatusCode.NoContent);
}
```
And the HTTP file being used to invoke the functions above.
```http
### GET request for working datetime test
GET http://localhost:7071/api/datetime/2024-09-16T09:15:40
### GET request for broken dateonly test
GET http://localhost:7071/api/dateonly/2024-09-16
### GET request for broken timeonly test
GET http://localhost:7071/api/timeonly/09:15:40
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at src/DotNetWorker.Core/Converters/JsonPocoConverter.cs around line 66, then follow binding through DefaultFunctionInputBindingFeature.cs around line 97. Reproduce the DateOnly and TimeOnly routes shown in the issue and compare them with DateTime binding. Done means the dateonly and timeonly requests bind successfully to their parameters without FunctionInputConverterException.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100