2012-06-08 11 views
21

ho trovato davvero un bel filtro di azione che converte un parametro separato da virgole a una lista di tipo generico: http://stevescodingblog.co.uk/fun-with-action-filters/Converti il ​​filtro azione personalizzato per l'utilizzo dell'API Web?

vorrei usarlo, ma non funziona per un ApiController, si ignorano completamente. Qualcuno può aiutare a convertire questo per l'utilizzo dell'API Web?

[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; 
    } 
} 

risposta

26

Quale spazio dei nomi si utilizza per ActionFilterAttribute? Per le API Web è necessario utilizzare lo spazio dei nomi System.Web.Http.Filters e non System.Web.Mvc.

EDIT

Ecco una conversione approssimativa, non è completamente testato.

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; 
     } 
    } 
} 

richiesta Esempio

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

Vorrei che fosse così facile. Ho appena provato ma non verrà compilato. Non riconosce filterContext.ActionParameters, filterContext.RequestContext e filterContext.RouteData. – TruMan1

+0

Ho modificato la mia risposta per includere alcuni esempi di codice – Jonathan

+2

'System.Web.HttpFilters' ->' System.Web.Http.Filters' –

3

Ecco Un'altra opzione:

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; 
    } 
} 

E elencare il param nel ApiController ActionMethod:

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

Dove StudentIds è il parametro querystring (&StudentIds=1,2,4)

Problemi correlati