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

WriteAsJsonAsync throws exception when using NewtonsoftJson serializer #2735

Open
CSharpBender opened this issue Sep 25, 2024 · 2 comments
Open
Labels
Needs: Author Feedback Needs: Triage (Functions) potential-bug Items opened using the bug report template, not yet triaged and confirmed as a bug

Comments

@CSharpBender
Copy link

Description

In Azure Functions isolated model an exception is thrown when overriding the serializer from ConfigureFunctionsWebApplication() and response.WriteAsJsonAsync() is used: System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.
I followed the Microsoft guide for migrating an in-process Azure function to the isolated model which clearly states that I need to override the Json serializer in ConfigureFunctionsWebApplication and use services.AddMvc().AddNewtonsoftJson().
While searching for a fix to my issue I found issue1 and issue2

Steps to reproduce

  1. Create an Azure Function
 [Function("MyIsolatedFunction")]
        public async Task<HttpResponseData> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestData request)
        { 
            var response = request.CreateResponse(HttpStatusCode.OK);
            await response.WriteAsJsonAsync(new { Name="Error"});
            return response;
 }
  1. Override Json serializer in Program.cs
public static class Program
{
    public static void Main(string[] args)
    {
        var host = new HostBuilder()
                .ConfigureFunctionsWebApplication((IFunctionsWorkerApplicationBuilder builder) =>
                {
                    builder.Services.Configure<WorkerOptions>(workerOptions =>
                    {
                        //<PackageReference Include="Microsoft.Azure.Core.NewtonsoftJson" Version="1.0.0" />
                        var settings = NewtonsoftJsonObjectSerializer.CreateJsonSerializerSettings();
                        settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                        settings.NullValueHandling = NullValueHandling.Ignore;

                        workerOptions.Serializer = new NewtonsoftJsonObjectSerializer(settings);
                    });
                })
                .ConfigureServices(services =>
                {
                    services.AddApplicationInsightsTelemetryWorkerService();
                    services.ConfigureFunctionsApplicationInsights();
                    services.ConfigureSerializer();
                })
                .Build();

        host.Run();
    }
    public static IServiceCollection ConfigureSerializer(this IServiceCollection services)
    {
        services.AddMvc().AddNewtonsoftJson();
        return services;
    }
}
  1. Exception is thrown: System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.

  2. If I remove this line the exception disappears but the serialization is wrong because I'm using NewtonsoftJson attributes.
    workerOptions.Serializer = new NewtonsoftJsonObjectSerializer(settings); also this breaks the swagger definition.
    Another workaround is to serialize the object myself and use the WriteAsync method instead of WriteAsJsonAsync

@CSharpBender CSharpBender added the potential-bug Items opened using the bug report template, not yet triaged and confirmed as a bug label Sep 25, 2024
@snerte
Copy link

snerte commented Oct 1, 2024

I'm having thesame exception when using:

        public async Task<HttpResponseData> HttpStart(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
        HttpRequestData reqData,
        [DurableClient] DurableTaskClient client)
        {
            var request = await reqData.ReadFromJsonAsync<MyModel>();

A bug for sure.

@liliankasem
Copy link
Member

Hi, I wasn't able to repro the issue you reported. Could you try again with all the latest packages?

Here is what I tried which ended up working without issues (v1 of the worker):

csproj (functions version v4 | .net8)

  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Core.NewtonsoftJson" Version="2.0.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.23.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.18.1" />
  </ItemGroup>

Program.cs

    public static void Main(string[] args)
    {
        var host = new HostBuilder()
                .ConfigureFunctionsWorkerDefaults((IFunctionsWorkerApplicationBuilder builder) =>
                {
                    builder.Services.Configure<WorkerOptions>(workerOptions =>
                    {
                        var settings = NewtonsoftJsonObjectSerializer.CreateJsonSerializerSettings();
                        settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                        settings.NullValueHandling = NullValueHandling.Ignore;

                        workerOptions.Serializer = new NewtonsoftJsonObjectSerializer(settings);
                    });
                })
                .Build();

        host.Run();
    }

MyHttpFunction.cs

        [Function("MyIsolatedFunction")]
        public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestData request)
        {
            var response = request.CreateResponse(HttpStatusCode.OK);
            await response.WriteAsJsonAsync(new { Name="Error"});
            return response;
        }

Core Tools

Azure Functions Core Tools
Core Tools Version:       4.0.6610 Commit hash: N/A +0d55b5d7efe83d85d2b5c6e0b0a9c1b213e96256 (64-bit)
Function Runtime Version: 4.1036.1.23224

[2024-11-14T22:17:04.539Z] Found /Users/likasem/source/repros/dni-newtonsoft-exception-repro/dni-newtonsoft-exception-repro.csproj. Using for user secrets file configuration.
[2024-11-14T22:17:08.219Z] Worker process started and initialized.

Functions:

        MyIsolatedFunction: [GET,POST] http://localhost:7071/api/MyIsolatedFunction

For detailed output, run func with --verbose flag.
[2024-11-14T22:17:11.409Z] Executing 'Functions.MyIsolatedFunction' (Reason='This function was programmatically called via the host APIs.', Id=7cf54d0c-a0a9-4599-a546-7cde2d241c0c)
[2024-11-14T22:17:11.934Z] Executed 'Functions.MyIsolatedFunction' (Succeeded, Id=7cf54d0c-a0a9-4599-a546-7cde2d241c0c, Duration=551ms)
[2024-11-14T22:17:13.356Z] Host lock lease acquired by instance ID '000000000000000000000000303B82C0'.

New Model

It's also worth noting we have a new model we suggest folks update to:

csproj (functions version v4 | .net8)

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Azure.Core.NewtonsoftJson" Version="2.0.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.0" />
  </ItemGroup>

Program.cs

using Azure.Core.Serialization;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

var builder = FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

builder.Services.Configure<WorkerOptions>(workerOptions =>
{
    var settings = NewtonsoftJsonObjectSerializer.CreateJsonSerializerSettings();
    settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    settings.NullValueHandling = NullValueHandling.Ignore;

    workerOptions.Serializer = new NewtonsoftJsonObjectSerializer(settings);
});

builder.Build().Run();

MyHttpFunction.cs

        [Function("MyHttpFunction")]
        public IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");
            return new OkObjectResult(new { Name="Error"});
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Author Feedback Needs: Triage (Functions) potential-bug Items opened using the bug report template, not yet triaged and confirmed as a bug
Projects
None yet
Development

No branches or pull requests

3 participants