Convert custom action filter for Web API use?

24,290

Solution 1

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

Solution 2

Here is another way:

public class ConvertCommaDelimitedList<T> : CollectionModelBinder<T>
{
    public override bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
   {
       var _queryName = HttpUtility.ParseQueryString(actionContext.Request.RequestUri.Query)[bindingContext.ModelName];
        List<string> _model = new List<string>();
        if (!String.IsNullOrEmpty(_queryName))
            _model = _queryName.Split(',').ToList();

        var converter = TypeDescriptor.GetConverter(typeof(T));
        if (converter != null)
            bindingContext.Model = _model.ConvertAll(m => (T)converter.ConvertFromString(m));
        else
            bindingContext.Model = _model;

        return true;
    }
}

And list your param in the ApiController ActionMethod:

[ModelBinder(typeof(ConvertCommaDelimitedList<decimal>))] List<decimal> StudentIds = null)

Where StudentIds is the querystring param (&StudentIds=1,2,4)

Share:
24,290
TruMan1
Author by

TruMan1

Updated on July 09, 2022

Comments

  • TruMan1
    TruMan1 almost 2 years

    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;
        }
    }
    
  • TruMan1
    TruMan1 almost 12 years
    I wish it was that easy. I just tried but it won't compile. It does not recognize filterContext.ActionParameters, filterContext.RequestContext, and filterContext.RouteData.
  • Scotty.NET
    Scotty.NET over 10 years
    System.Web.HttpFilters --> System.Web.Http.Filters
  • Admin
    Admin almost 10 years
    is there any way to Get variable value in filter which is defined in get method or any method of web api controller in web api project