11

我正在关注有关 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

然后它起作用了——我得到一个包含一个元素的数组。

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

4

1 回答 1

14

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

如果您期望 'ids' 来自查询字符串,请修改您的路由模板,如下所示(因为您定义它的方式使得 uri 路径中的 'ids' 是强制性的):

[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;
    }
}
于 2013-07-22T23:07:17.500 回答