2017-10-12 9 views
0

私はドットネットコアのルートから(私の場合は長い)パラメータのリストを解析しようとしています。したがって、私はこのようなものが欲しいですドットネットコアルートリストのパラメータバインド

[HttpGet("{ids?}")] 
public async Task<IActionResult> Get([FromRoute, Optional]long[] ids) 
{ 
} 

私はそれがデフォルトで動作しないことを知っています、私はそれがクエリ文字列を介して動作することも知っています。しかし、一貫性のために、私はそれを経路パラメータとして保持したいと思います。

ドットネットコアでFromRouteを拡張してこの動作を実装することはできますか?

これまで、技術的に動作するアクションフィルタを作成することができましたが、余分な属性が必要です。デフォルトのFromRouteはモデル状態でエラーを作成しています(特に最後の部分は明らかに許容できません)。

私の現在の属性部分のコードは、適切な実装のために再利用可能かもしれません。

public class ArrayInputAttribute : ActionFilterAttribute 
{ 
    private readonly List<string> _ParameterNames; 
    public string Separator { get; set; } 

    public ArrayInputAttribute(params string[] parameterName) 
    { 
     _ParameterNames = parameterName.ToList(); 
     Separator = ","; 
    } 

    public void ProcessArrayInput(ActionExecutingContext actionContext, string parameterName) 
    { 
     if (actionContext.ActionArguments.ContainsKey(parameterName)) 
     { 
      var parameterDescriptor = actionContext.ActionDescriptor.Parameters.FirstOrDefault(p => p.Name == parameterName); 
      if (parameterDescriptor != null && parameterDescriptor.ParameterType.IsArray) 
      { 
       var type = parameterDescriptor.ParameterType.GetElementType(); 
       var parameters = String.Empty; 
       if (actionContext.RouteData.Values.ContainsKey(parameterName)) 
       { 
        parameters = (string)actionContext.RouteData.Values[parameterName]; 
       } 
       else 
       { 
        var queryString = actionContext.HttpContext.Request.Query; 
        if (queryString[parameterName].Count > 0) 
        { 
         parameters = queryString[parameterName]; 
        } 
       } 
       try 
       { 
        var values = parameters.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries) 
        .Select(TypeDescriptor.GetConverter(type).ConvertFromString).ToArray(); 
        var typedValues = Array.CreateInstance(type, values.Length); 
        values.CopyTo(typedValues, 0); 
        actionContext.ActionArguments[parameterName] = typedValues; 
       } 
       catch (System.Exception) 
       { 
        (actionContext.Controller as Controller).ViewData.ModelState.AddModelError(parameterDescriptor.Name, ""); 
       } 
      } 
     } 
    } 

    public override void OnActionExecuting(ActionExecutingContext actionContext) 
    { 
     _ParameterNames.ForEach(parameterName => ProcessArrayInput(actionContext, parameterName)); 
    } 
} 

あなたはこの

[HttpGet("{ids?}")] 
[ArrayInput("ids")] 
[Produces(typeof(TestWebResponseDTO))] 
public async Task<IActionResult> Get(long[] ids) 
{ 
} 
+0

長くする必要がありますか[]かカスタムDTOが動作しますか? CustomModelBindersはおそらくこれを行うことができるからです。 – MindingData

+0

なぜあなたは 'HttpPost'を使用していないのか分かりません。 URLには文字の制限があります – LP13

+0

@MindingDataそれは最も理にかなっているようにlong/intでなければなりません。興味深いカスタムモデルバインダーについては、配列やリストではうまくいかないでしょうか? –

答えて

0

のようにそれを使用することができ、私は正直にそれがより多くの人が前に、この問題が発生していない可能性があるかを理解していません。私は怠け者になり、答えを探しますが、いつものように、それを自分でスイングするのが最善の考えです。ここで完成した、プロトタイプのやり方ですが、これまでのところ私はそれに満足しています。

使用:

[HttpGet("{ids:" + RouteArrayConstants.NUMBER_ARRAY + "}")] 
[Produces(typeof(TestWebResponseDTO))] 
public async Task<IActionResult> Get([FromRoute, Required]long[] ids) 
{ 
} 

ArrayBinder:

public class RouteArrayModelBinder : IModelBinder 
{ 
    private char separator; 
    public RouteArrayModelBinder(char Separator = ',') 
    { 
     separator = Separator; 
    } 

    public async Task BindModelAsync(ModelBindingContext bindingContext) 
    { 
     if (bindingContext == null) 
      throw new ArgumentNullException(nameof(bindingContext)); 

     var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 

     if (valueProviderResult != ValueProviderResult.None) 
     { 
      var valueAsString = valueProviderResult.FirstValue; 
      try 
      { 
       var type = bindingContext.ModelType.GetElementType(); 
       var values = valueAsString.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries) 
       .Select(TypeDescriptor.GetConverter(type).ConvertFromString).ToArray(); 
       var typedValues = Array.CreateInstance(type, values.Length); 
       values.CopyTo(typedValues, 0); 
       bindingContext.Result = ModelBindingResult.Success(typedValues); 
      } 
      catch (System.Exception) 
      { 
       bindingContext.Result = ModelBindingResult.Failed(); 
       bindingContext.ModelState.AddModelError(bindingContext.ModelName, $@"Failed to convert ""{valueAsString}"" to ""{bindingContext.ModelType.FullName}"""); 
      } 
     } 
    } 
} 

ArrayBinderProvider:

public class RouteArrayModelBinderProvider : IModelBinderProvider 
{ 
    public IModelBinder GetBinder(ModelBinderProviderContext context) 
    { 
     if (context == null) 
     { 
      throw new ArgumentNullException(nameof(context)); 
     } 

     if (context.Metadata.ModelType.IsArray) 
     { 
      return new RouteArrayModelBinder(); 
     } 

     return null; 
    } 
} 

定数:

public static class RouteArrayConstants 
{ 
    public const string NUMBER_ARRAY = "regex(^\\d+(,\\d+)*$)"; 

    public const string STRING_ARRAY = "regex(^\\s+(,\\s+)*$)"; 
} 

セットアップ:

services.AddMvc(cfg => 
{ 
    cfg.ModelBinderProviders.Insert(0, new RouteArrayModelBinderProvider()); 
}); 

追記: あなたは、その闊歩仕様軍ルートパラメータが必要とされることがありません(あなたがすべき)あなたのAPIを文書化する闊歩を使用している場合。そのため、IDに関係なくすべてのリソースを取得したい場合は、IDなしの特別なアクションが必要です。

関連する問題