我尝试在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中,我可以使用命名空间[JsonConverter(typeof(StringEnumConverter))]
中的Newtonsoft.Json
装饰枚举,以实现此目的。现在这行不通。
我知道.Net Core 3已切换到System.Text.Json
命名空间中的转换器,因此我尝试使用命名空间[JsonConverter(typeof(JsonStringEnumConverter))]
中的System.Text.Json.Serialization
装饰相同的枚举。这也不起作用。
所有其他类似的问题或者说上面的应该起作用,或者像this所说的那样,通过在.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行,选择了格式化程序。有趣的是,当我在这一点上中断时,选择的格式化程序是NewtonsoftJsonOutputFormatter!我认为第一个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
中。我以前从未遇到过。通常,该程序包将是一个临时依赖项,因此我可以引用它而不必明确地依赖于自己的程序包,因此我不太确定这里发生了什么。这是正常现象,还是我的环境有问题?
当我以另一种方式删除它时,没关系,无论如何它都被选中了,所以似乎MvcOptions
中注入的DefaultOutputFormatterSelector
与我们可以配置的MvcOptions
不同在启动中。
在这一点上,我用尽了线索,转向了你们。我希望有人可以指出正确的方向。
答案 0 :(得分:4)
我可以使用下面的代码来使它工作
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上的netcoreapp3.1中(3.0.2534提交哈希:bc1e9efa8fa78dd1a138dd1ac1ebef97aac8d78e)和函数运行时版本:3.0.13353.0,带有以下软件包:
<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))]正常工作。我不确定最近是否发生了更新,但是为了以防万一,我将保留上述解决方案。
答案 1 :(得分:2)
我还遇到了.NET Core 3.1 Function App的序列化问题。建议我使用此应用程序设置作为临时解决方法:
"FUNCTIONS_V2_COMPATIBILITY_MODE": true
这解决了我的issue。
答案 2 :(得分:0)
请尝试Enum.GetName
var result = Enum.GetName(typeof(TestEnum), TestEnum.TestValue);