【问题标题】:How to create one model class to use with multiple API methods that require different JSON payloads?如何创建一个模型类以与需要不同 JSON 有效负载的多个 API 方法一起使用?
【发布时间】:2020-03-18 20:42:35
【问题描述】:

我在一项服务下有多个 API。 API 采用不同的 JSON 有效负载。我想创建一个具有属性和值的模型类并设置它们。这可以通过一个模型类用于多个 API 来实现吗?例如,我有一个 API 方法采用 JSON id 和名称,另一个 API 采用 userID 和颜色。在将请求发送到需要这些数据的特定 API 时,如何指示要应用哪些特定属性和值?我在我的发送请求中使用JsonConvert.SerializeObject(Model)

目前我的模型中有以下内容。我正在使用 TagModel 类的实例来分配值,但是当使用 SerializeForApiMethod 并说明我需要哪个方法时,它需要分配整个模型值。我正在尝试使用特定方法名称提取我需要的那些。

 public class TagModel
    {

        public static TagModel model = new TagModel
        {
            endpointIds = new List<int> { -2147483612, -2147483611 },
            tagIds = new List<int> { 35, 37 },
            id = -2147483639,
            parentId = 37
        };

        [UseWithApiMethods("UpdateEndpointsToTags")]
        public List<int> endpointIds { get; set; }
        public List<int> tagIds { get; set; }

        [UseWithApiMethods("UpdateEndpointsFromTags")]
         public int id { get; set; }
         public int parentId { get; set; }
    } 


This is m helper class:
```public class Helper
    {

        [AttributeUsage(AttributeTargets.Property)]
        public class UseWithApiMethodsAttribute : Attribute
        {
            public UseWithApiMethodsAttribute(params string[] methodNames)
            {
                MethodNames = methodNames;
            }

            public string[] MethodNames { get; private set; }
        }

        public class SelectivePropertyResolver : DefaultContractResolver
        {
            public string ApiMethodName { get; private set; }

            public SelectivePropertyResolver(string apiMethodName)
            {
                ApiMethodName = apiMethodName;
            }

            protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
            {
                JsonProperty prop = base.CreateProperty(member, memberSerialization);
                if (member.MemberType == MemberTypes.Property)
                {
                    var att = ((PropertyInfo)member).GetCustomAttribute<UseWithApiMethodsAttribute>(true);
                    if (att != null && !att.MethodNames.Contains(ApiMethodName))
                    {
                        prop.Ignored = true;
                    }
                }
                return prop;
            }
        }

        public  string SerializeForApiMethod(Object model, string methodName)
        {
            var settings = new JsonSerializerSettings
            {
                ContractResolver = new SelectivePropertyResolver(methodName),
                Formatting = Formatting.Indented
            };
            return JsonConvert.SerializeObject(model, settings);
        }
    }

This is my Method class using the SerializeForApiMethod method
```public HttpWebResponse UpdateEndpointsFromTags()
        {
            RequestHandler requestor = new RequestHandler(BaseUrl + "UpdateEndpointsFromTags", HttpVerb.POST, AuthenticationType.Bearer);
            return requestor.SendRequest(helper.SerializeForApiMethod(model , "UpdateEndpointsFromTags"));            
        }

        public HttpWebResponse UpdateEndpointsToTags()
        {
            RequestHandler requestor = new RequestHandler(BaseUrl + "UpdateEndpointsToTags", HttpVerb.POST, AuthenticationType.Bearer);
              return requestor.SendRequest(helper.SerializeForApiMethod(model, "UpdateEndpointsToTags"));      
        }

复杂的负载

【问题讨论】:

  • 从您的示例看来,API 彼此完全不同。如果是这种情况,我不确定我是否理解通过为他们使用相同的模型可以获得什么。你能详细解释一下你为什么要这样做吗?
  • 在框架中,我们有一个模型文件夹,其中包含每个服务的所有模型。例如,我们有一个名为 User 的服务。在用户服务中,我有一个 API 创建了一个用户,另一个 API 更新了一个用户。两者都采用不同的 Json 有效负载。我可以在一个父模型类下创建不同的模型类吗?我试图避免创建这么多模型

标签: c# api testing json.net nunit


【解决方案1】:

您可以在序列化期间使用自定义ContractResolver 选择性地包含或排除属性。您可以引入自定义属性来指示模型中的哪些属性应包含在哪些 API 方法中。然后让解析器查找这些属性并忽略与您要序列化的 API 方法不匹配的模型属性。

属性代码:

[AttributeUsage(AttributeTargets.Property)]
public class UseWithApiMethodsAttribute : Attribute
{
    public UseWithApiMethodsAttribute(params string[] methodNames)
    {
        MethodNames = methodNames;
    }

    public string[] MethodNames { get; private set; }
}

解析器代码:

public class SelectivePropertyResolver : DefaultContractResolver
{
    public string ApiMethodName { get; private set; }

    public SelectivePropertyResolver(string apiMethodName)
    {
        ApiMethodName = apiMethodName;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        if (member.MemberType == MemberTypes.Property)
        {
            var att = ((PropertyInfo)member).GetCustomAttribute<UseWithApiMethodsAttribute>(true);
            if (att == null || !att.MethodNames.Contains(ApiMethodName))
            {
                prop.Ignored = true;
            }
        }
        return prop;
    }
}

然后,像这样使用新属性标记您的模型,例如:

public class Model
{
    [UseWithApiMethods("method1", "method3")]
    public int id { get; set; }

    [UseWithApiMethods("method1")]
    public string name { get; set; }

    [UseWithApiMethods("method2")]
    public int userId { get; set; }

    [UseWithApiMethods("method2")]
    public string color { get; set; }
}

最后,创建一个使用解析器序列化模型的辅助方法:

public static string SerializeForApiMethod(object model, string methodName)
{
    var settings = new JsonSerializerSettings
    {
        ContractResolver = new SelectivePropertyResolver(methodName),
        Formatting = Formatting.Indented
    };
    return JsonConvert.SerializeObject(model, settings);
}

这样称呼,例如:

string json = SerializeForApiMethod(model, "method1");

这是一个工作演示:https://dotnetfiddle.net/vfXCqP

【讨论】:

  • 非常感谢您的帮助! :) 我有一个问题需要澄清,我们能否使 SerializeForApiMethod 方法动态地应用于任何模型类,而不是在参数中说“模型模型”。因为我有很多模型课
  • 我创建了一个辅助类,并将 Attribute、Resolver 和 SerializeForApiMethod 方法放在该类中。
  • @ABIDKHAN 是的,当然,SerializeForApiMethod 中的model 参数只能是object 类型。我已经更新了我的答案和小提琴。
  • 在我的模型类中,我使用 UseWithApiMethods 进行获取和设置。但后来我创建了一个类模型的实例来传递值。所以我创建了两个 UseWithApiMethods 和一个模型类的实例。但是当我使用 SerializeForApiMethod(model, "methodname") 时,它会获取所有值而不仅仅是方法值
  • 请你看看原来的问题,我编辑了我应该如何分配值的一些想法,所以它只序列化那些使用指定方法名称的人
猜你喜欢
  • 2017-07-28
  • 2020-10-17
  • 2016-01-14
  • 2021-01-19
  • 1970-01-01
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 2016-02-17
相关资源
最近更新 更多