如何在Web API请求的FromBody ViewModel中使用具有EnumMember属性的枚举? [英] How to use enums with EnumMember attribute in FromBody ViewModel in Web API Request?

查看:68
本文介绍了如何在Web API请求的FromBody ViewModel中使用具有EnumMember属性的枚举?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在具有[FromBody]视图模型和枚举的ASP.NET Core Web API项目中实现HttpPost方法.过去,使用[FromBody]属性绑定视图模型效果很好.

I'm trying to implement an HttpPost method in an ASP.NET Core Web API project with a [FromBody] view model and enums. In the past, binding view models with the [FromBody] attribute worked well.

在我的特定情况下,我想提供一个JSON端点,它将给定值转换为具有不同名称的C#枚举.这个例子应该解释我想要实现的目标:

In my particular scenario, I want to offer a JSON endpoint where I convert a given value into a C# enum with different names. This example should explain what I want to achieve:


    public enum WeatherEnum
    {
        [EnumMember(Value = "good")]
        Good,

        [EnumMember(Value = "bad")]
        Bad
    }

内部,我想使用WeatherEnum.GoodWeatherEnum.Bad,并且端点的使用者希望使用小写值.因此,我试图将要在JSON主体中传递的值映射到我的Enum表示形式.

Internally, I want to use WeatherEnum.Good and WeatherEnum.Bad and the consumer of my endpoint wants to use lowercase values. Therefore, I'm trying to map the values which will be passed in the JSON body to my Enum representation.

我已经了解了EnumMember属性和StringEnumConverter.我已经从新的ASP.NET Core Web API 3.0模板创建了一个最小的示例(您需要添加这些NuGet包Microsoft.Extensions.DependencyInjectionMicrosoft.AspNetCore.Mvc.NewtonsoftJsonNewtonsoft.Json)

I've read about the EnumMember attribute and StringEnumConverter. I've created a minimal example from the new ASP.NET Core Web API 3.0 template (You need to add these NuGet packages Microsoft.Extensions.DependencyInjection, Microsoft.AspNetCore.Mvc.NewtonsoftJson, and Newtonsoft.Json)

ConfigureServices :

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
    }).SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
    .AddNewtonsoftJson(json =>
    {
        json.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
        json.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
    });
    services.AddControllers();
}

WheatherForecastController :

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Serialization;

namespace WebAPITestEnum.Controllers
{
    [ApiController]
    [Produces("application/json")]
    [Consumes("application/json")]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        [HttpPost]
        [Route("method")]
        public ActionResult<QueryResponseClass> TestMethod([FromBody] QueryRequestClass request)
        {
            // do something with the request ...

            return new QueryResponseClass()
            {
                Foo = "bar"
            };
        }
    }

    public class QueryRequestClass
    {
        public WeatherEnum Weather { get; set; }
    }

    public class QueryResponseClass
    {
        public string Foo { get; set; }
    }


    [JsonConverter(typeof(StringEnumConverter))]
    public enum WeatherEnum
    {
        [EnumMember(Value = "good")]
        Good,

        [EnumMember(Value = "bad")]
        Bad
    }
}

我的端点正被邮递员打来,其尸体如下

My endpoint is being called from Postman with the following body

{
  "Weather": "good"
}

这将导致此错误:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|245d862e-4ab01d3956be5f60.",
    "errors": {
        "$.Weather": [
            "The JSON value could not be converted to WebAPITestEnum.Controllers.WeatherEnum. Path: $.Weather | LineNumber: 1 | BytePositionInLine: 18."
        ]
    }
}

感觉我只在某处缺少一行.是否可以在具有FromBody属性的视图模型中使用枚举?

It feels like I'm only missing one line somewhere. It is possible to use Enums in view models with the FromBody attribute?

推荐答案

我发布的问题中的代码确实有效.在最小样本中,我忘记在枚举上设置[Required]属性.但是,然后我有一个问题,如果未设置值,该方法应如何反应.正确地(?)假定了枚举的默认值,这不是我想要的.

The code from the question that I posted is indeed valid. In my minimal sample, I forgot to set the [Required] attribute on the enum. However, then I had the problem how the method should react if then value is not set. It correctly(?) assumed the default value of the enum which is not what I wanted.

我四处搜寻并找到了此解决方案 https://stackoverflow.com/a/54206737/225808 枚举是可为null的值,这不理想,但至少我有验证信息,如果缺少该值,则会收到错误消息

I searched around and found this solution https://stackoverflow.com/a/54206737/225808 The enum is nullable which is not ideal but at least I have the validation and I get an error message if the value is missing

更新/警告:您可以使用上面引用的解决方案,但是!似乎代码可以编译,但会抛出问题的错误消息.我进一步将自己的项目与测试项目进行了比较,发现我还需要两个包含2个NuGet程序包,以使所有功能正常工作:

Update/Warning: You can use the solution referenced above, but! It seems that the code will compile but instead throws the error message from question. I further compared my own project with the test project and noticed that I also needed two include 2 NuGet packages in order to make everything work:

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson
  • Newtonsoft.Json

似乎Microsoft.AspNetCore.Mvc.NewtonsoftJson覆盖了默认行为?如果有人可以对此有所说明,我很乐意为您提供帮助.

It seems that Microsoft.AspNetCore.Mvc.NewtonsoftJson overrides from default behavior? If someone can shed some light on this, I'd glady appreciate it.

更新2 :我还更新了引用的so解决方案,以基于EnumMemberAttribute解析枚举值:

Update 2: I also updated the referenced so solution to parse the enum value based on the EnumMemberAttribute:

[JsonConverter(typeof(CustomStringToEnumConverter<WeatherEnum>))]
public enum WeatherEnum
{
    [EnumMember(Value = "123good")]
    Good,

    [EnumMember(Value = "bad")]
    Bad
}

public class CustomStringToEnumConverter<T> : StringEnumConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (string.IsNullOrEmpty(reader.Value?.ToString()))
        {
            return null;
        }
        try
        {
            return EnumExtensions.GetValueFromEnumMember<T>(reader.Value.ToString());
        }
        catch (Exception ex)
        {
            return null;
        }
    }
}

public static class EnumExtensions
{
    public static T GetValueFromEnumMember<T>(string value)
    {
        var type = typeof(T);
        if (!type.IsEnum) throw new InvalidOperationException();
        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(EnumMemberAttribute)) as EnumMemberAttribute;
            if (attribute != null)
            {
                if (attribute.Value == value)
                    return (T)field.GetValue(null);
            }
            else
            {
                if (field.Name == value)
                    return (T)field.GetValue(null);
            }
        }
        throw new ArgumentException($"unknow value: {value}");
    }
}

这篇关于如何在Web API请求的FromBody ViewModel中使用具有EnumMember属性的枚举?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆