使用 Azure 函数 3.0 中的属性将枚举序列化为字符串



我尝试在Azure Functions 3.0/3.1应用程序中获取HTTP触发器以返回枚举的字符串表示形式,但没有任何运气。我已经尝试了Core 3.0和Core 3.1。

给定此类:

public enum TestEnum
{
TestValue
}
public class TestClass
{ 
public TestEnum Test { get; set; }
}

我希望这个HTTP触发器:

[FunctionName("MyHttpTrigger")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
return new OkObjectResult(new TestClass { Test = TestEnum.TestValue });
}

返回{ "test": "TestValue" }.相反,它返回{ "test": 0 }

在 .Net Core 2 中,我可以使用命名空间Newtonsoft.Json中的[JsonConverter(typeof(StringEnumConverter))]来修饰枚举来实现这一点。这现在行不通。

我知道 .Net Core 3 切换到System.Text.Json-命名空间中的转换器,所以我尝试用命名空间System.Text.Json.Serialization中的[JsonConverter(typeof(JsonStringEnumConverter))]装饰相同的枚举。这也行不通。

所有其他类似的问题要么说上面应该有效,要么像这样,说通过在.AddControllers().AddMvc()链中配置 JsonOptions 来解决它,但对于不起作用的函数应用。函数运行时负责设置控制器,因此我们无法直接访问进一步的配置 AFAIK。

使用核心工具的准系统 Azure Functions 项目应该可以重现此问题:

func init MyFunctionProj
cd MyFunctionProj
func new --name MyHttpTrigger --template "HttpTrigger"

然后更改 http 触发器以返回内部带有枚举的对象。

我的版本:

> func --version
3.0.1975
> dotnet --version
3.1.100

其他信息

我已经尝试了多种解决方法,所以这是我的一些观察结果。OkObjectResult由 ObjectResultExecutor 处理。我尝试将实现复制到我的解决方案中并将其添加到 DI 容器中,以便我可以调试:

builder.Services.AddSingleton<IActionResultExecutor<ObjectResult>, TestExecutor>();

在链接源的第 101 行,选择格式化程序。有趣的是,当我在这一点上中断时,选择的格式化程序是NewtonsoftJsonOutput格式化程序!我认为第一个JsonConverter属性应该适用于这个,但它没有。

我认为修改可用输出格式化程序列表是值得一试的。ActionResultExecutor 使用 DefaultOutputFormatterSelector 进行选择,进而注入IOptions<MvcOptions>以获取输出格式化程序。

为了迫使DefaultOutputFormatterSelector做出不同的选择,我尝试了这个:

class MvcOptionsConfiguration : IPostConfigureOptions<MvcOptions> 
{
...
}
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.ConfigureOptions<MvcOptionsConfiguration>();
}
}

请注意,常规IConfigureOptions<T>不能使用,因为options.Outputformatters集合缺少除Microsoft.AspNetCore.Mvc.WebApiCompatShim.HttpResponseMessageOutputFormatter之外的所有其他格式化程序,这本身就很奇怪(为什么一个全新的 3.0 应用程序使用兼容性填充程序?

我尝试使用以下方法从集合中删除类型NewtonsoftJsonOutputFormatter

options.OutputFormatters.RemoveType<NewtonsoftJsonOutputFormatter>();

为此,我必须引用 nuget 包Microsoft.AspNetCore.Mvc.NewtonsoftJson才能访问该类型,但引用它时的类型与运行时使用的类型不同。 运行时使用的类型的type.Assembly.CodeBase位于%USERPROFILE%/AppData/Local/AzureFunctionsTools/,但来自 nuget 的 位于Project root/bin/Debug。我以前从未遇到过这种情况。通常,包将是一个暂时的依赖项,所以我可以在不明确依赖包的情况下引用它,所以我不太确定这里发生了什么。这是正常的,还是我的环境中有问题?

当我以另一种方式删除它时,它无关紧要,无论如何它都被选中了,所以似乎注入DefaultOutputFormatterSelectorMvcOptions与我们可以在启动中配置MvcOptions不同。

在这一点上,我用完了线索,转向你们。我希望有人能为我指出正确的方向。

我能够使用以下代码使其工作

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Converters;
[assembly: FunctionsStartup(typeof(Configs.Startup))]
namespace Configs
{
class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddMvcCore().AddNewtonsoftJson(x =>
{
x.SerializerSettings.Converters.Add(new StringEnumConverter());
});
}
}
}

这是在 Azure Functions Core Tools(3.0.2534 提交哈希:bc1e9efa8fa78dd1a138dd1ac1ebef97aac8d78e(和函数运行时版本:3.0.13353.0 上的 netcoreapp3.1 中,具有以下包:

<PackageReference Include="AsyncEnumerator" Version="4.0.2" />
<PackageReference Include="AzureFunctions.Autofac" Version="4.0.0" />
<PackageReference Include="CsvHelper" Version="15.0.5" />
<PackageReference Include="Dapper" Version="2.0.35" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.4" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.1" />

希望这对某人有所帮助。

我在这里推送了一个示例存储库:https://github.com/rawrspace/string-enum-example

编辑:我今天再次使用相同的设置使用它,并使用[JsonConverter(typeof(StringEnumConverter((]工作得很好。我不确定最近是否发生了更新,但为了以防万一,我将保留上述解决方案。

我还遇到了.NET Core 3.1函数应用的序列化问题。建议我使用此应用程序设置作为临时解决方法:

"FUNCTIONS_V2_COMPATIBILITY_MODE": true

这解决了我的问题。

最新更新