C#WebAPI路由按POST值类型

时间:2015-12-29 11:48:16

标签: c# post asp.net-web-api

我有一个创建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来处理不同的通知。所以请不要这样做。

2 个答案:

答案 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:我将文字字符串移到其他地方,所以我不会在解决方案中指定它们两次。