将 json 数组传递给 REST Web API

Posted

技术标签:

【中文标题】将 json 数组传递给 REST Web API【英文标题】:Passing a json array to a REST Web API 【发布时间】:2021-12-31 07:44:01 【问题描述】:

这是一个两部分的问题。

    我想将一个 int 值数组传递给我的 REST url,但我得到了一个内联约束异常。我尝试定义的路线如下

[Route("categories/categoryId:int/documenttypes/documentTypes:int[]")]

... ...

当被调用时,它看起来像这样:

   api/categories/2/documenttypes/[2,3,4,5]

谁能指出我如何处理这个内联约束异常的正确方向。我找到了几篇文章,但似乎没有一篇文章适用于如何处理 int 值数组。

也许它不可行,我应该将其更改为 POST 请求,但我想我会先尝试使用 GET 或仅使用字符串参数,然后将另一个资源定义添加到我的 REST url,例如

   api/config/categories/2/documenttypes/multi?doctype=[1,3,4]

    根据 REST 标准,此事件是否可接受?

    api/categories/2/documenttypes/[1,3,4]
    

我知道我可以将它作为查询字符串传递,但我遇到的问题是,如果我这样做,我最终会遇到 2 个端点冲突:

    api/categories/2/documenttypes

无参数并返回给定类别的所有文档类型

    api/categories/2/documenttypes?doctypes=[1,2,3]

当定义然后调用时,我得到一个错误,即有多个端点具有相同的定义,因此请求查看我是否可以以某种方式将其作为内联约束传递。

希望以上内容有意义。如果没有,请走开。谢谢

【问题讨论】:

【参考方案1】:

我找出了需要更改的各个部分。

    首先,我必须添加一个新的路由约束 (IHttpRouteConstraint) 来处理作为 Web API REST 资源的一部分传递的新数据类型:

     public class HttpRouteConstraintIntArray : IHttpRouteConstraint
     
          public bool Match(HttpRequestMessage request, IHttpRoute route,
            string parameterName, IDictionary<string, object> values,
            HttpRouteDirection routeDirection)
          
              object value;
              if (values.TryGetValue(parameterName, out value) && value != null)
              
                  var parameters = value.ToString()
                      .Replace("", string.Empty)
                      .Replace("", string.Empty)
                      .Replace("[", string.Empty)
                      .Replace("]", string.Empty);
                  var type = typeof(int);
                  var newvalues = parameters.Split(new[]  "," ,
                   StringSplitOptions.RemoveEmptyEntries)                 
                  .Select(TypeDescriptor.GetConverter(type)
                  .ConvertFromString).ToArray();
    
                  var typedValues = Array.CreateInstance(type, newvalues.Length);
    
                  newvalues.CopyTo(typedValues, 0);
    
                  return true;    
              
    
              return false;
          
     
    

一旦我声明了这个新约束,我还必须在 WebApiConfig.cs 中注册它:

        var constraintResolver = new DefaultInlineConstraintResolver();
        constraintResolver.ConstraintMap.Add("intarray", 
           typeof(HttpRouteConstraintIntArray));

           config.MapHttpAttributeRoutes(constraintResolver);

但是一旦我做了这些 stanges,我又开始犯另一个错误:

    the request contains an entity body but no Content-Type header
    get request ....

    为了解决这个错误,我不得不在端点定义中添加 [FromUri]:

    [HttpGet]  [Route("categories/categoryId:int/documenttypes/
               documentTypeIds:intarray")]
     public async Task<IHttpActionResult> GetDataByDocumentTypeIds(
     int categoryId,[FromUri] int[] documentTypeIds)
    

现在上面已经通过了这两个错误,但由于某种原因,documentTypeIds 以数组的形式返回,但它只包含一个值而不是 3,例如它的值是 0 而不是 1,所以显然需要添加别的东西。

    为了处理最后一个问题,我必须创建一个动作过滤器属性:

     public class ArrayInputAttribute : ActionFilterAttribute
     
     private readonly string[] _parameternames;
    
     public string Separator  get; set; 
    
     public ArrayInputAttribute(params string[] parameternames)
     
         this._parameternames = parameternames;
         Separator = "-";
     
    
     public void ProcessArrayInput(HttpActionContext actionContext, string parametername)
     
         if (actionContext.ActionArguments
               .ContainsKey(parametername))
         
             var parameterdescriptor = actionContext.ActionDescriptor.GetParameters()
                 .FirstOrDefault(p => p.ParameterName == parametername);
    
             if (parameterdescriptor != null && parameterdescriptor.ParameterType.IsArray)
             
                 var type = parameterdescriptor.ParameterType.GetElementType();
                 var parameters = string.Empty;
                 if (actionContext.ControllerContext.RouteData.Values
                       .ContainsKey(parametername))
                 
                     parameters = (string)actionContext.ControllerContext
                       .RouteData.Values[parametername];
                 
                 else
                 
                     var queryString = actionContext.ControllerContext
                        .Request.RequestUri.ParseQueryString();
                     if (queryString[parametername] != null)
                     
                         parameters = queryString[parametername];
                     
                 
    
                 parameters = parameters.ToString()
                     .Replace("", string.Empty)
                     .Replace("", string.Empty)
                     .Replace("[", string.Empty)
                     .Replace("]", string.Empty);
    
                 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;
             
         
     
    
     public override void OnActionExecuting(HttpActionContext actionContext)
     
         foreach (var parameterName in _parameternames)
         
             ProcessArrayInput(actionContext, parameterName);
         
     
    
    

最后,我必须将属性添加到端点定义中:

    [HttpGet]
    [Route("categories/categoryId:int/documenttypes/
            documentTypeIds:intarray")]
    [ArrayInputAttribute("documentTypeIds", Separator = ",")]
    public async Task<IHttpActionResult> GetDataByDocumentTypeIds(int categoryId,
    [FromUri] int[] documentTypeIds)
    
    

几点说明:

    为了在 *** 中半体面地显示,一些行被拆分,显然在 .NET studio 中不起作用

    上面的代码是我在谷歌搜索后最终找到的几篇文章的组合。它们是:

-Pass an array of integers to ASP.NET Web API?

从表面上看,我什至可能不需要约束,我想我现在没有时间调查,因为我需要继续前进,我会在稍后尝试时发布更新。

Web API Attribute Routing Route Constraints

Web API Attribute Routing Route Constrains

希望以上内容对其他人有所帮助。

谢谢。

【讨论】:

【参考方案2】:

假设动作是

[HttpGet("~/api/categories/id/documenttypes")]
public ActionResult GetDocs(int id, int[] doctype)

         if(doctype.Length >  0)
         
            ....

           else ...

使用这个网址

api/categories/2/documenttypes?doctype=2&doctype=3&doctype=4&doctype=5

【讨论】:

您错过了我说使用此方法存在重复冲突的部分,因为 2 个端点最终会拥有相同的路由 @Thierry 参数不会改变路线 稍后我会再次检查它,但是当我有 api/categories/2/documenttypes 时,它需要返回所有文档类型,并且当我指定 api/categories/2/documenttypes?doctype= 1&doctype=2 等...我应该只返回这些。是否只是检查是否提供了参数并基于此调用不同的服务函数的情况? @Thierry 我在代码中添加了数组检查 太棒了!我希望摆脱这个臃肿的代码:)

以上是关于将 json 数组传递给 REST Web API的主要内容,如果未能解决你的问题,请参考以下文章

如何将 JSON 传递给 REST API (IBKR)

我可以将 JSON 传递给这个 REST API 调用吗?阿帕奇点燃

如何在 JAVA 中将 JSON 和文件传递给 REST API?

将 JSON 数组传递给 WCF Web 服务

需要使用 JSON 为 REST v2 API 传递 Jasper 报告参数的示例

将数组传递给 asp net core web api 操作方法 HttpGet