WebAPI 多个 Put/Post 参数

我试图在 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参数吗?它总是设置为 null。但我能用 DataContractJsonSerializer从尸体上恢复数据。

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

453517 次浏览

本地 WebAPI 不支持多个 POST 参数的绑定。正如科林指出,有一些限制,概述了我的 博客文章他参考。

通过创建自定义参数绑定器有一个变通方法。这样做的代码很难看,也很复杂,但我已经在我的博客上发布了代码以及详细的解释,准备插入到这里的一个项目中:

将多个简单的 POST 值传递给 ASP.NET Web API

这个案例的路由模板是什么样子的?

你发布了这个网址:

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

为了使这个工作,我希望像这样的路由在您的 WebApiConfig:

routeTemplate: {controller}/{offerId}/prices/

其他假设包括: 你的控制器叫 OffersController。 - 在请求体中传递的 JSON 对象是 OfferPriceParameters类型(不是任何派生类型) 你在控制器上没有任何其他方法可以干扰这个方法(如果有,试着注释掉它们,看看会发生什么)

正如菲利普提到的,如果你开始接受一些回答,这将有助于你的问题,因为“接受率为0%”可能会让人们认为他们是在浪费时间

我们通过 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"
}]
}}
...

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

例如:

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

通过使用来自 https://github.com/keith5000/MultiPostParameterBinding的 MultiPostParameter terBinding 类,可以允许多个 POST 参数

使用方法:

1)下载 < a href = “ https://github.com/keith5000/MultiPostParameter terBinding/tree/master/Source”rel = “ norefrer”> Source 文件夹中的代码,并将其添加到您的 Web API 项目或解决方案中的任何其他项目中。

2)对需要支持多个 POST 参数的操作方法使用 [多重后置参数]属性。

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

3)将 Global.asax.cs 中的这一行添加到 Application _ Start 方法中 之前的任何位置,对 GlobalConfiguration.Configure (WebApiConfig.Register)的调用:

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) { ... });

有关详细信息,请访问 链接

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

一个简单的参数类可以用来在一篇文章中传递多个参数:

public class AddCustomerArgs
{
public string First { get; set; }
public string Last { get; set; }
}


[HttpPost]
public IHttpActionResult AddCustomer(AddCustomerArgs args)
{
//use args...
return Ok();
}
[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);
}
});

很好的问题和评论——从这里的回复中学到了很多:)

作为一个附加的例子,请注意,您也可以混合身体和路线,例如。

[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

如果您不想使用 ModelBinding 方式,可以使用 DTO 为您完成这项工作。例如,在 DataLayer 中创建一个 POST 操作,该操作接受复杂类型并从 BusinessLayer 发送数据。您可以在 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 中的操作可以创建为:

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

从业务层调用:

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

现在,如果我想一次发送多个 Student 的数据,这仍然可以工作。像下面这样修改 MyAction。不需要编写[ FromBody ] ,WebAPI2默认采用复杂类型[ FromBody ]。

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

然后在调用它时,传递一个 List<StudentCurriculumDTO>数据。

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

请求参数,比如

enter image description here

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

您可以获得字符串形式的格式数据:

    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://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-2

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

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

实际上在控制器中不可能使用多个参数。 如果你说“为什么我不能?”我必须用这个来回答你的问题:

此规则的原因是,请求主体可能存储在只能读取一次的非缓冲流中。 你也可以读这个 文章

现在我们知道为什么不能在多个参数中使用 body 了,那么解是什么呢? 解决方案应该使用类。 您可以创建该类,并像使用属性一样在该类中使用这些参数,然后在 API 的输入中使用该类! 这也是在[ FromBody ]中使用多个参数的最佳方式。 但如果我找到了另一种方式,我会说。