如何使用Web API中的属性路由通过URI发送数组?

时间:2013-07-22 22:07:16

标签: asp.net-web-api routes asp.net-web-api-routing

我正在关注article on Attribute Routing in Web API 2尝试通过URI发送数组:

[HttpPost("api/set/copy/{ids}")]
public HttpResponseMessage CopySet([FromUri]int[] ids)

这在使用基于约定的路由时起作用:

http://localhost:24144/api/set/copy/?ids=1&ids=2&ids=3

但是使用属性路由它不再有效 - 我找不到404。

如果我试试这个:

http://localhost:24144/api/set/copy/1

然后它工作 - 我得到一个包含一个元素的数组。

如何以这种方式使用属性路由?

1 个答案:

答案 0 :(得分:13)

您注意到的行为与行动选择和行动选择更为相关。模型绑定而不是属性路由。

如果您希望“ID”来自查询字符串,那么请修改您的路由模板,如下所示(因为您定义它的方式会使uri路径中的'ID'成为必需的):

[HttpPost("api/set/copy")]

看看你的第二个问题,你是否希望接受uri内部的id列表,如api/set/copy/[1,2,3]?如果是的话,我不认为web api内置了对这种模型绑定的支持。

你可以实现像下面这样的自定义参数绑定来实现它(我猜有其他更好的方法来实现这一点,比如通过模型绑定器和值提供者,但我不太了解它们......所以你可能需要探索这些选项):

[HttpPost("api/set/copy/{ids}")]
public HttpResponseMessage CopySet([CustomParamBinding]int[] ids)

示例:

[AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
public class CustomParamBindingAttribute : ParameterBindingAttribute
{
    public override HttpParameterBinding GetBinding(HttpParameterDescriptor paramDesc)
    {
        return new CustomParamBinding(paramDesc);
    }
}

public class CustomParamBinding : HttpParameterBinding
{
    public CustomParamBinding(HttpParameterDescriptor paramDesc) : base(paramDesc) { }

    public override bool WillReadBody
    {
        get
        {
            return false;
        }
    }

    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, 
                                                    CancellationToken cancellationToken)
    {
        //TODO: VALIDATION & ERROR CHECKS
        string idsAsString = actionContext.Request.GetRouteData().Values["ids"].ToString();

        idsAsString = idsAsString.Trim('[', ']');

        IEnumerable<string> ids = idsAsString.Split(',');
        ids = ids.Where(str => !string.IsNullOrEmpty(str));

        IEnumerable<int> idList = ids.Select(strId =>
            {
                if (string.IsNullOrEmpty(strId)) return -1;

                return Convert.ToInt32(strId);

            }).ToArray();

        SetValue(actionContext, idList);

        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
        tcs.SetResult(null);
        return tcs.Task;
    }
}