Convert custom action filter for Web API use?

TruMan1 picture TruMan1 · Jun 8, 2012 · Viewed 24k times · Source

I found a really nice action filter that converts a comma-separated parameter to a generic type list: http://stevescodingblog.co.uk/fun-with-action-filters/

I would like to use it but it will not work for an ApiController, it completely ignore it. Can someone help convert this for Web API use?

[AttributeUsage(AttributeTargets.Method)]
public class SplitStringAttribute : ActionFilterAttribute
{
    public string Parameter { get; set; }

    public string Delimiter { get; set; }

    public SplitStringAttribute()
    {
        Delimiter = ",";
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.ActionParameters.ContainsKey(this.Parameter))
        {
            string value = null;
            var request = filterContext.RequestContext.HttpContext.Request;

            if (filterContext.RouteData.Values.ContainsKey(this.Parameter)
                && filterContext.RouteData.Values[this.Parameter] is string)
            {
                value = (string)filterContext.RouteData.Values[this.Parameter];
            }
            else if (request[this.Parameter] is string)
            {
                value = request[this.Parameter] as string;
            }

            var listArgType = GetParameterEnumerableType(filterContext);

            if (listArgType != null && !string.IsNullOrWhiteSpace(value))
            {
                string[] values = value.Split(Delimiter.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

                var listType = typeof(List<>).MakeGenericType(listArgType);
                dynamic list = Activator.CreateInstance(listType);

                foreach (var item in values)
                {
                    try
                    {
                        dynamic convertedValue = TypeDescriptor.GetConverter(listArgType).ConvertFromInvariantString(item);
                        list.Add(convertedValue);
                    }
                    catch (Exception ex)
                    {
                        throw new ApplicationException(string.Format("Could not convert split string value to '{0}'", listArgType.FullName), ex);
                    }
                }

                filterContext.ActionParameters[this.Parameter] = list;
            }
        }

        base.OnActionExecuting(filterContext);
    }

    private Type GetParameterEnumerableType(ActionExecutingContext filterContext)
    {
        var param = filterContext.ActionParameters[this.Parameter];
        var paramType = param.GetType();
        var interfaceType = paramType.GetInterface(typeof(IEnumerable<>).FullName);
        Type listArgType = null;

        if (interfaceType != null)
        {
            var genericParams = interfaceType.GetGenericArguments();
            if (genericParams.Length == 1)
            {
                listArgType = genericParams[0];
            }
        }

        return listArgType;
    }
}

Answer

Jonathan picture Jonathan · Jun 8, 2012

What namespace are you using for ActionFilterAttribute? For Web API you need to be using System.Web.Http.Filters namespace and not System.Web.Mvc.

EDIT

Here's a rough conversion, not fully tested.

SplitStringAttribute

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace StackOverflowSplitStringAttribute.Infrastructure.Attributes
{
    [AttributeUsage(AttributeTargets.Method)]
    public class SplitStringAttribute : ActionFilterAttribute
    {
        public string Parameter { get; set; }

        public string Delimiter { get; set; }

        public SplitStringAttribute()
        {
            Delimiter = ",";
        }

        public override void OnActionExecuting(HttpActionContext filterContext)
        {
            if (filterContext.ActionArguments.ContainsKey(Parameter))
            {
                var qs = filterContext.Request.RequestUri.ParseQueryString();
                if (qs.HasKeys())
                {
                    var value = qs[Parameter];

                    var listArgType = GetParameterEnumerableType(filterContext);

                    if (listArgType != null && !string.IsNullOrWhiteSpace(value))
                    {
                        string[] values = value.Split(Delimiter.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

                        var listType = typeof(List<>).MakeGenericType(listArgType);
                        dynamic list = Activator.CreateInstance(listType);

                        foreach (var item in values)
                        {
                            try
                            {
                                dynamic convertedValue = TypeDescriptor.GetConverter(listArgType).ConvertFromInvariantString(item);
                                list.Add(convertedValue);
                            }
                            catch (Exception ex)
                            {
                                throw new ApplicationException(string.Format("Could not convert split string value to '{0}'", listArgType.FullName), ex);
                            }
                        }

                        filterContext.ActionArguments[Parameter] = list;
                    }
                }
            }

            base.OnActionExecuting(filterContext);
        }

        private Type GetParameterEnumerableType(HttpActionContext filterContext)
        {
            var param = filterContext.ActionArguments[Parameter];
            var paramType = param.GetType();
            var interfaceType = paramType.GetInterface(typeof(IEnumerable<>).FullName);
            Type listArgType = null;

            if (interfaceType != null)
            {
                var genericParams = interfaceType.GetGenericArguments();
                if (genericParams.Length == 1)
                {
                    listArgType = genericParams[0];
                }
            }

            return listArgType;
        }

    }
}

CsvController

using System.Web.Http;
using System.Collections.Generic;
using StackOverflowSplitStringAttribute.Infrastructure.Attributes;

namespace StackOverflowSplitStringAttribute.Controllers
{
    public class CsvController : ApiController
    {

        // GET /api/values

        [SplitString(Parameter = "data")]
        public IEnumerable<string> Get(IEnumerable<string> data)
        {
            return data;
        }
    }
}

Example request

http://localhost:52595/api/csv?data=this,is,a,test,joe