我有一个创建Web API的请求,该API能够接受POST请求并根据参数中收到的数据类型(DataAvailableNotification vs ExpiredNotification)采取不同的操作。
我创建了一个ApiController并公开了两种方法:
[HttpPost]
public void DataAvailable(DataAvailableNotification dataAvailable,
[FromUri] string book, [FromUri] string riskType)
{
}
[HttpPost]
public void DataAvailable(ExpiredNotification dataAvailable,
[FromUri] string book, [FromUri] string riskType)
{
}
public class DataAvailableNotification
{
[JsonProperty(PropertyName = "$type")]
public string RdfType { get { return "App.RRSRC.Feeds.DataAvailable"; } }
public string SnapshotRevisionId { get; set; }
public string[] URLs { get; set; }
public string ConsumerId { get; set; }
public Guid ChannelId { get; set; }
}
public class ExpiredNotification
{
[JsonProperty(PropertyName = "$type")]
public string RdfType { get { return "Service.Feeds.Expired"; } }
public string ConsumerId { get; set; }
public Guid ChannelId { get; set; }
}
然而,他们根本没有被召唤。
如果我注释掉其中一个通知到达控制器但我无法正确处理通知类型(假设两个通知都将映射到相同的方法)。
有没有办法配置Web API来查看POSTed值的类型并调用最佳匹配控制器方法?
PS:我不能拥有2个不同的URL来处理不同的通知。所以请不要这样做。
答案 0 :(得分:1)
根据类型使用一个操作和过滤器。 我通过使用反射并执行以下操作来解决相同的问题。
[HttpPost]
public void DataAvailable([FromBody]IDictionary<string, string> dataAvailable,
[FromUri] string book, [FromUri] string riskType) {
if(dataAvailable != null && dataAvailable.ContainsKey("$type") {
var type = dataAvaliable["$type"];
if(type == "App.RRSRC.Feeds.DataAvailable"){
DataAvailableNotification obj = createInstanceOf<DataAvailableNotification>(dataAvailable);
DataAvailable(obj,book,riskType);
} else if (type == "Service.Feeds.Expired") {
ExpiredNotification obj = createInstanceOf<ExpiredNotification>(dataAvailable);
DataAvailable(obj,book,riskType);
}
}
}
private void DataAvailable(DataAvailableNotification dataAvailable, string book, string riskType) {
}
private void DataAvailable(ExpiredNotification dataAvailable, string book, string riskType) {
}
private T createInstanceOf<T>(IDictionary<string, string> data) where T : class, new() {
var result = new T();
var type = typeof(T);
//map properties
foreach (var kvp in data) {
var propertyName = kvp.Key;
var rawValue = kvp.Value;
var property = type.GetProperty(propertyName);
if (property != null && property.CanWrite) {
property.SetValue(result, rawValue );
}
}
return result;
}
答案 1 :(得分:1)
我解决的解决方案类似于@Nikosi和@jpgrassi建议的解决方案。
在控制器中,我创建了一个通知点:
[HttpPost]
public void Notify(BaseNotification notification,
[FromUri] string book, [FromUri] string riskType)
{
DataAvailableNotification dataAvailableNotification;
ExpiredNotification expiredNotification;
if ((dataAvailableNotification = notification as DataAvailableNotification) != null)
{
HandleDataAvailableNotification(dataAvailableNotification);
}
else if ((expiredNotification = notification as ExpiredNotification) != null)
{
HandleExpiredNotification(expiredNotification);
}
}
private void HandleDataAvailableNotification(DataAvailableNotification dataAvailableNotification)
{
}
private void HandleExpiredNotification(ExpiredNotification expiredNotification)
{
}
BaseNotification是所有通知的基类:
public abstract class BaseNotification
{
[JsonProperty(PropertyName = "$type")]
public abstract string RdfType { get; }
public string ConsumerId { get; set; }
public Guid ChannelId { get; set; }
}
创建了一个JsonConverter:
public class RdfNotificationJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var resultJson = JObject.Load(reader);
var rdfType = resultJson["$type"].ToObject<string>();
BaseNotification result;
switch (rdfType)
{
case "App.RRSRC.Feeds.DataAvailable":
{
result = new DataAvailableNotification
{
SnapshotRevisionId = resultJson["SnapshotRevisionId"].ToObject<string>(),
URLs = resultJson["URLs"].ToObject<string[]>()
};
break;
}
case "Service.Feeds.Expired":
{
result = new ExpiredNotification();
break;
}
default:
{
throw new NotSupportedException();
}
}
result.ChannelId = resultJson["ChannelId"].ToObject<Guid>();
result.ConsumerId = resultJson["ConsumerId"].ToObject<string>();
return result;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(BaseNotification);
}
}
并在配置中注册了新转换器:
public static void Configure(HttpSelfHostConfiguration config)
{
Throw.IfNull(config, "config");
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new RdfNotificationJsonConverter());
}
我更喜欢这个解决方案,因为我在控制器中有实际类型,转换器处理丑陋的反序列化部分(也更可测试)。
PS:我将文字字符串移到其他地方,所以我不会在解决方案中指定它们两次。