在Asp.Net Core ActionFilter中替换RouteData值不会影响[ApiController]验证

时间:2019-08-28 00:08:57

标签: c# asp.net-core .net-core

我正在实现HashId,目的是将实体ID透明地转换为短哈希以供API使用。

目标:

  • /api/tag/dY7je的请求应具有/api/tag/1的效果(或已将其转换为)
  • 控制器操作仍应要求intpublic ActionResult<TagViewModel> Get(int id)

问题:

我创建了一个ActionFilterAttribute来拦截请求,并将hash转换为适当的id,并更改RouteData以使其匹配。但是,即使在更改RouteData之后,任何使用[ApiController]的控制器仍会返回错误消息"The value 'dY7je' is not valid."如果我删除了[ApiController],修改后的值将传递到控制器操作,一切都很好。

问题:

[ApiController]正在查看哪些数据,我可以通过ActionFilter进行修改以避免出现此问题,我该怎么做?我已经搜索了ActionExecutingContext,但是找不到那里的值。

我的粗糙动作过滤器:

    public class HashIdFilterAttribute : ActionFilterAttribute, IOrderedFilter
    {
        public new int Order => int.MinValue;

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            foreach ((string paramKey, object value) in context.RouteData.Values)
            {
                if (paramKey.EndsWith("id", StringComparison.OrdinalIgnoreCase))
                {
                    int id = HashIdsUtility.DecodeInt(value as string);
                    context.RouteData.Values[paramKey] = HashIdsUtility.DecodeInt(id);
                    context.ActionArguments[paramKey] = HashIdsUtility.DecodeInt(id);
                }
            }

            base.OnActionExecuting(context);
        }
    }

1 个答案:

答案 0 :(得分:0)

解决方案是使用在任何ResourceFilter之前执行的ActionFiltersMSDocs

    public class HashIdFilterAttribute : ActionFilterAttribute, IOrderedFilter, IResourceFilter
    {
        public new int Order => int.MinValue;

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            return;
        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            ProcessRouteData(context.RouteData);
        }

        private void ProcessRouteData(RouteData routeData)
        {
            foreach ((string paramKey, object value) in routeData.Values)
            {
                if (paramKey.EndsWith("id", StringComparison.OrdinalIgnoreCase))
                {
                    routeData.Values[paramKey] = HashIdsUtility.DecodeInt(value as string);
                }
            }
        }
    }