WebAPI 多个 Put/Post 参数

Posted

技术标签:

【中文标题】WebAPI 多个 Put/Post 参数【英文标题】:WebAPI Multiple Put/Post parameters 【发布时间】:2013-01-02 16:00:44 【问题描述】:

我正在尝试在 WebAPI 控制器上发布多个参数。一个参数来自 URL,另一个来自正文。这是网址: /offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

这是我的控制器代码:

public HttpResponseMessage Put(Guid offerId, OfferPriceParameters offerPriceParameters)

    //What!?
    var ser = new DataContractJsonSerializer(typeof(OfferPriceParameters));
    HttpContext.Current.Request.InputStream.Position = 0;
    var what = ser.ReadObject(HttpContext.Current.Request.InputStream);

    return new HttpResponseMessage(HttpStatusCode.Created);

正文的内容是 JSON 格式:


    "Associations":
    
        "list": [
        
            "FromEntityId":"276774bb-9bd9-4bbd-a7e7-6ed3d69f196f",
            "ToEntityId":"ed0d2616-f707-446b-9e40-b77b94fb7d2b",
            "Types":
            
                "list":[
                
                    "BillingCommitment":5,
                    "BillingCycle":5,
                    "Prices":
                    
                        "list":[
                        
                            "CurrencyId":"274d24c9-7d0b-40ea-a936-e800d74ead53",
                            "RecurringFee":4,
                            "SetupFee":5
                        ]
                    
                ]
            
        ]
    

知道为什么默认绑定无法绑定到我的控制器的offerPriceParameters 参数吗?它始终设置为空。但我可以使用DataContractJsonSerializer 从正文中恢复数据。

我也尝试使用参数的FromBody 属性,但它也不起作用。

【问题讨论】:

【参考方案1】:
[HttpPost]
public string MyMethod([FromBody]JObject data)

    Customer customer = data["customerData"].ToObject<Customer>();
    Product product = data["productData"].ToObject<Product>();
    Employee employee = data["employeeData"].ToObject<Employee>();
    //... other class....

使用参考

using Newtonsoft.Json.Linq;

使用 JQuery Ajax 请求

var customer = 
    "Name": "jhon",
    "Id": 1,
;
var product = 
    "Name": "table",
    "CategoryId": 5,
    "Count": 100
;
var employee = 
    "Name": "Fatih",
    "Id": 4,
;

var myData = ;
myData.customerData = customer;
myData.productData = product;
myData.employeeData = employee;

$.ajax(
    type: 'POST',
    async: true,
    dataType: "json",
    url: "Your Url",
    data: myData,
    success: function (data) 
        console.log("Response Data ↓");
        console.log(data);
    ,
    error: function (err) 
        console.log(err);
    
);

【讨论】:

很好的解决方案。如果其他人还不清楚,如果您从 ajax 调用中传递简单的多个参数,也可以使用 .ToObject()、.ToObject()、.ToString() 等。跨度> 谢谢,我已经通过创建自己的 API 并通过 Postman 对其进行测试来尝试了您的解决方案,并且工作正常;但是我添加了第四个参数,例如 var test= "Name" : "test " 并将其添加到 myData 对象并发送成功;有没有办法避免这种情况并只限制原始对象? @H.Al 不,Newtonsoft.Json 可以包含库知道的任何关于翻译的 json 数据。您无法阻止发送数据。这取决于你使用传入的数据 如果你没有正确适配 ConfigureServices(); 这会产生一个严重的错误。 ***.com/questions/60831768/…现在也是老方案了,有更好的选择,见下文。【参考方案2】:

WebAPI 本身不支持绑定多个 POST 参数。正如 Colin 指出的那样,我的 blog post 他引用了一些限制。

有一种解决方法是创建自定义参数绑定器。执行此操作的代码既丑陋又令人费解,但我已经在我的博客上发布了代码以及详细说明,准备在这里插入项目:

Passing multiple simple POST Values to ASP.NET Web API

【讨论】:

所有功劳归于你 :) 当这个问题出现时,我恰好在开始我自己的实现时正在阅读你关于 WebAPI 的系列文章。 从 2019 年开始,现在可以了。 @John - 是否有支持此功能的基本版本?今天没有任何成功。【参考方案3】:

如果正在使用属性路由,则可以使用 [FromUri] 和 [FromBody] 属性。

例子:

[HttpPost()]
[Route("api/products/id:int")]
public HttpResponseMessage AddProduct([FromUri()] int id,  [FromBody()] Product product)

  // Add product

【讨论】:

我使用了完全相同的方法。我需要将两个模型传递给动作。我通过查询字符串和正文传递了一个具有较少属性的属性。此外,您不需要明确指定 [FromBody] 属性字节 我做不出来,你有更完整的例子吗? 我认为这不是通过 POST 方法发送数据的正确方法,但如果您必须通过 post 发送 2 个模型,我看不到其他解决方案。 这个答案就是果酱! 我使用的是aspnetcore,你必须使用[FromRoute]而不是[FromUri]【参考方案4】:

我们通过 HttpPost 方法传递 Json 对象,并在动态对象中解析它。它工作正常。这是示例代码:

webapi:

[HttpPost]
public string DoJson2(dynamic data)

   //whole:
   var c = JsonConvert.DeserializeObject<YourObjectTypeHere>(data.ToString()); 

   //or 
   var c1 = JsonConvert.DeserializeObject< ComplexObject1 >(data.c1.ToString());

   var c2 = JsonConvert.DeserializeObject< ComplexObject2 >(data.c2.ToString());

   string appName = data.AppName;
   int appInstanceID = data.AppInstanceID;
   string processGUID = data.ProcessGUID;
   int userID = data.UserID;
   string userName = data.UserName;
   var performer = JsonConvert.DeserializeObject< NextActivityPerformers >(data.NextActivityPerformers.ToString());

   ...

复杂的对象类型可以是对象、数组和字典。

ajaxPost:
...
Content-Type: application/json,
data: "AppName":"SamplePrice",
       "AppInstanceID":"100",
       "ProcessGUID":"072af8c3-482a-4b1c‌​-890b-685ce2fcc75d",
       "UserID":"20",
       "UserName":"Jack",
       "NextActivityPerformers":
           "39‌​c71004-d822-4c15-9ff2-94ca1068d745":[
                 "UserID":10,
                 "UserName":"Smith"
           ]
       
...

【讨论】:

我们可以将多个参数格式化为一个json对象进行post,稍后在服务器端解析成多个对象。这可能是另一种思考方式。 @EkoosticMartin,它工作正常,你需要使用解析动态类型: JsonConvert.DeserializeObject(data.ToString()); 一个复杂的数据内容示例在这里,它包括数组和字典对象。 "AppName":"SamplePrice","AppInstanceID":"100","ProcessGUID":"072af8c3-482a-4b1c-890b-685ce2fcc75d","UserID":"20","UserName":"Jack"," NextActivityPerformers":"39c71004-d822-4c15-9ff2-94ca1068d745":["UserID":10,"UserName":"Smith"] 好吧,那就只用一个字符串参数,没有差异。 单一并不意味着简单,json字符串可以与许多不同类型的对象组合。这是重点,也是解决问题的另一种方式。 优秀的解决方案!谢谢:)【参考方案5】:

一个简单的参数类可以用来在一个帖子中传递多个参数:

public class AddCustomerArgs

    public string First  get; set; 
    public string Last  get; set; 


[HttpPost]
public IHttpActionResult AddCustomer(AddCustomerArgs args)

    //use args...
    return Ok();

【讨论】:

您知道示例 POST 请求的外观吗? @NadiaSolovyeva,它不仅仅是一个查询字符串,因为 POSTED 信息在正文中,而不是查询字符串。我喜欢用 PostMan 做测试查询,然后你就可以看到它到底长什么样了。 没关系,我已经找到了方法。 POST 头:内容类型:应用程序/json; POST 正文: "First":"1", "Last":"1000" 【参考方案6】:

很好的问题和 cmets - 从这里的回复中学到了很多 :)

作为另一个示例,请注意,您还可以混合使用 body 和 routes,例如

[RoutePrefix("api/test")]
public class MyProtectedController 

    [Authorize]
    [Route("id/id")]
    public IEnumerable<object> Post(String id, [FromBody] JObject data)
    
        /*
          id                                      = "123"
          data.GetValue("username").ToString()    = "user1"
          data.GetValue("password").ToString()    = "pass1"
         */
    

这样调用:

POST /api/test/id/123 HTTP/1.1
Host: localhost
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer x.y.z
Cache-Control: no-cache

username=user1&password=pass1


enter code here

【讨论】:

我想发送 2 个复杂类型参数。像as[HttpPost] public string UploadFile(UploadMediaFile mediaFile, byte[] datas) 怎么办。【参考方案7】:

您可以使用来自https://github.com/keith5000/MultiPostParameterBinding 的 MultiPostParameterBinding 类来允许多个 POST 参数

使用它:

1) 下载 Source 文件夹中的代码并将其添加到您的 Web API 项目或解决方案中的任何其他项目中。

2) 在需要支持多个 POST 参数的操作方法上使用属性 [MultiPostParameters]

[MultiPostParameters]
public string DoSomething(CustomType param1, CustomType param2, string param3)  ... 

3) 在调用 GlobalConfiguration.Configure(WebApiConfig.Register) 之前 将 Global.asax.cs 中的这一行添加到 Application_Start 方法中:

GlobalConfiguration.Configuration.ParameterBindingRules.Insert(0, MultiPostParameterBinding.CreateBindingForMarkedParameters);

4) 让您的客户将参数作为对象的属性传递。 DoSomething(param1, param2, param3) 方法的示例 JSON 对象是:

 param1: Text:"" , param2: Text:"" , param3:"" 

示例 JQuery:

$.ajax(
    data: JSON.stringify( param1: Text:"" , param2: Text:"" , param3:"" ),
    url: '/MyService/DoSomething',
    contentType: "application/json", method: "POST", processData: false
)
.success(function (result)  ... );

访问the link了解更多详情。

免责声明:我直接与链接资源相关联。

【讨论】:

【参考方案8】:

2021 年,有新的解决方案。 Pradip Rupareliya 提出了一个很好的建议,我将仅使用 Dict 来补充,而不是像他那样使用辅助数据结构:

[HttpPost]
public ActionResult MakePurchase([FromBody] Dictionary<string, string> data)

    try
    
        int userId = int.Parse(data["userId"]);
        float boughtAmountInARS = float.Parse(data["boughtAmountInARS"]);
        string currencyName = data["currencyName"];
    
    catch (KeyNotFoundException)
    
        return BadRequest();
    
    catch (FormatException)
    
        return BadRequest();
    

【讨论】:

【参考方案9】:

对于这种情况,您的 routeTemplate 是什么样的?

你发布了这个网址:

/offers/40D5E19D-0CD5-4FBD-92F8-43FDBB475333/prices/

为了使其正常工作,我希望您的WebApiConfig 中有这样的路由:

routeTemplate: controller/offerId/prices/

其他假设是: - 你的控制器叫做OffersController。 - 您在请求正文中传递的 JSON 对象的类型为 OfferPriceParameters(不是任何派生类型) - 您在控制器上没有任何其他可能干扰此方法的方法(如果有,请尝试将它们注释掉,看看会发生什么)

正如菲利普所说,如果您开始接受一些答案,这将对您的问题有所帮助,因为“接受率为 0%”可能会让人们认为他们在浪费时间

【讨论】:

我的路线是“offers/offerId/prices”。这是我的控制器中唯一的方法。【参考方案10】:

如果您不想采用 ModelBinding 方式,可以使用 DTO 为您完成此操作。例如,在 DataLayer 中创建一个接受复杂类型并从 BusinessLayer 发送数据的 POST 操作。在 UI->API 调用的情况下可以这样做。

这里是示例 DTO。将教师分配给学生并将多篇论文/主题分配给学生。

public class StudentCurriculumDTO
 
     public StudentTeacherMapping StudentTeacherMapping  get; set; 
     public List<Paper> Paper  get; set; 
     
public class StudentTeacherMapping
 
     public Guid StudentID  get; set; 
     public Guid TeacherId  get; set; 
 

public class Paper
 
     public Guid PaperID  get; set; 
     public string Status  get; set; 
 

那么DataLayer中的action可以创建为:

[HttpPost]
[ActionName("MyActionName")]
public async Task<IHttpActionResult> InternalName(StudentCurriculumDTO studentData)
  
     //Do whatever.... insert the data if nothing else!
  

从 BusinessLayer 调用它:

using (HttpResponseMessage response = await client.PostAsJsonAsync("myendpoint_MyActionName", dataof_StudentCurriculumDTO)
  
     //Do whatever.... get response if nothing else!
  

现在,如果我想一次发送多个学生的数据,这仍然有效。修改MyAction,如下所示。 [FromBody] 不用写,WebAPI2 默认采用复杂类型[FromBody]。

public async Task<IHttpActionResult> InternalName(List<StudentCurriculumDTO> studentData)

然后在调用它时,传递一个List&lt;StudentCurriculumDTO&gt; 的数据。

using (HttpResponseMessage response = await client.PostAsJsonAsync("myendpoint_MyActionName", List<dataof_StudentCurriculumDTO>)

【讨论】:

【参考方案11】:

请求参数如

Web api 代码如下

public class OrderItemDetailsViewModel

    public Order order  get; set; 
    public ItemDetails[] itemDetails  get; set; 


public IHttpActionResult Post(OrderItemDetailsViewModel orderInfo)

    Order ord = orderInfo.order;
    var ordDetails = orderInfo.itemDetails;
    return Ok();

【讨论】:

【参考方案12】:

您可以将表单数据作为字符串获取:

    protected NameValueCollection GetFormData()
    
        string root = HttpContext.Current.Server.MapPath("~/App_Data");
        var provider = new MultipartFormDataStreamProvider(root);

        Request.Content.ReadAsMultipartAsync(provider);

        return provider.FormData;
    

    [HttpPost]
    public void test() 
    
        var formData = GetFormData();
        var userId = formData["userId"];

        // todo json stuff
    

https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-2

【讨论】:

以上是关于WebAPI 多个 Put/Post 参数的主要内容,如果未能解决你的问题,请参考以下文章

多个 CORS 适用于 GET 但不适用于 PUT/POST 与飞行前请求 Web api 2

PUT、POST 和 PATCH 有啥区别?

Web API系列统一异常处理

WebAPI从零开始学会使用WebAPI

WebAPI用法

跨域 WebAPI 批量请求