c# - 在 asp.net core api 中为复杂类型保持相同的休息端点
问题描述
我有一个 Rest 端点,我们称之为标签
它创建传递此 json 格式的标签对象:
[{
"TagName" : "IntegerTag",
"DataType" : 1,
"IsRequired" : true
}]
如果我想维护相同的端点来创建新标签但具有不同的 json 格式。假设我想创建一个 ListTag
[{
"TagName" : "ListTag",
"DataType" : 5,
"Values" : ["Value1", "Value2", "Value3"]
"IsRequired" : true
}]]
或 RangeTag
[{
"TagName" : "RangeTag",
"DataType" : 6,
"Min": 1,
"Max": 10,
"IsRequired" : true
}]
我对 C# 在我的控制器 api 上创建一个新的 Dto 并将其作为不同的参数传递没有任何问题,因为 C# 承认方法重载:
void CreateTags(TagForCreateDto1 dto){…}
void CreateTags(TagForCreateDto2 dto){…}
但是当我需要在同一个控制器中使用 POST 请求来维护这两种方法来创建标签时,mvc 不允许相同的路由同时拥有这两种方法。
[HttpPost]
void CreateTags(TagForCreateDto1 dto){…}
[HttpPost]
void CreateTags(TagForCreateDto2 dto){…}
处理请求时发生未处理的异常。AmbiguousActionException:匹配多个操作。以下操作匹配路由数据并满足所有约束。
请指教
解决方案
实现您想要的一种方法,即拥有一个POST endpoint
同时能够发布不同“版本”的方法Tags
是创建一个自定义JsonConverter
.
基本上,由于您已经拥有一个DataType
可用于识别Tag
它是哪种类型的属性,因此很容易将其序列化为正确的类型。所以,在代码中它看起来像这样:
BaseTag
> ListTag
,RangeTag
public class BaseTag
{
public string TagName { get; set; }
public int DataType { get; set; }
public bool IsRequired { get; set; }
}
public sealed class ListTag : BaseTag
{
public ICollection<string> Values { get; set; }
}
public sealed class RangeTag: BaseTag
{
public int Min { get; set; }
public int Max { get; set; }
}
然后,习俗PolymorphicTagJsonConverter
public class PolymorphicTagJsonConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
=> typeof(BaseTag).IsAssignableFrom(objectType);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
=> throw new NotImplementedException();
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader == null) throw new ArgumentNullException("reader");
if (serializer == null) throw new ArgumentNullException("serializer");
if (reader.TokenType == JsonToken.Null)
return null;
var jObject = JObject.Load(reader);
var target = CreateTag(jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
private BaseTag CreateTag(JObject jObject)
{
if (jObject == null) throw new ArgumentNullException("jObject");
if (jObject["DataType"] == null) throw new ArgumentNullException("DataType");
switch ((int)jObject["DataType"])
{
case 5:
return new ListTag();
case 6:
return new RangeTag();
default:
return new BaseTag();
}
}
}
繁重的工作是在ReadJson
和Create
方法上完成的。Create
接收一个JObject
并在其中检查DataType
属性以确定Tag
它是哪种类型。然后,继续为适当的ReadJson
调用。Populate
JsonSerializer
Type
然后您需要告诉框架使用您的自定义转换器:
[JsonConverter(typeof(PolymorphicTagJsonConverter))]
public class BaseTag
{
// the same as before
}
最后,您可以只拥有一个POST
可以接受所有类型标签的端点:
[HttpPost]
public IActionResult Post(ICollection<BaseTag> tags)
{
return Ok(tags);
}
一个缺点是switch
在转换器上。你可能会同意或不同意......你可以做一些聪明的工作并尝试让标签类以某种方式实现一些接口,这样你就可以调用Create
它BaseTag
,它会在运行时将调用转发给正确的接口,但我猜您可以开始使用它,如果复杂性增加,那么您可以考虑以更智能/更自动的方式找到正确的Tag
类。
推荐阅读
- ios - 禁用文本字段触摸但仍接受编辑?
- python - 如何使用 KFold 交叉验证输出作为 CNN 输入进行图像处理?
- c - 为什么我的输出在这两个测试用例中是错误的?
- matlab - MATLAB 图像拼接错误
- sql - 如何在 28 天的滑动窗口中统计不同的用户 - SQL Hive
- java - 在 Java 中使用 Sqoop 将 MYSQL 表导入 hdfs
- java - 如何在 browserstack 黄瓜 java 功能中并行运行?
- c# - C#模式传递什么相当于一个子类?
- node.js - 摩卡没有调用之前的方法,导致堆栈跟踪超过多个未调用的测试
- python - 如何让我的脚本使用我的虚拟环境而不是我的系统?