如何将json POST数据作为对象传递给Web API方法?

And*_*rus 293 javascript asp.net-mvc json asp.net-mvc-4 asp.net-web-api

ASP.NET MVC4 Web API应用程序定义了保存客户的post方法.客户在POST请求正文中以json格式传递.post方法中的customer参数包含属性的空值.

如何解决这个问题,以便发布的数据作为客户对象传递?

如果可能的话Content-Type:application/x-www-form-urlencoded应该使用,因为我不知道如何在发布表单的javascript方法中更改它.

控制器:

public class CustomersController : ApiController {

  public object Post([FromBody] Customer customer)
        {
            return Request.CreateResponse(HttpStatusCode.OK,
            new
            {
                customer = customer
            });
        }
    }
}

public class Customer
    {
        public string company_name { get; set; }
        public string contact_name { get; set; }
     }
Run Code Online (Sandbox Code Playgroud)

请求:

POST http://localhost:52216/api/customers HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8

{"contact_name":"sdfsd","company_name":"ssssd"}
Run Code Online (Sandbox Code Playgroud)

Shy*_*yju 510

编辑:31/10/2017

相同的代码/方法也适用于Asp.Net Core 2.0.主要区别在于,在asp.net核心中,web api控制器和Mvc控制器都合并到单个控制器模型中.所以,你的返回类型可能是IActionResult或它的一个实现(例如:OkObjectResult)


使用

contentType:"application/json"
Run Code Online (Sandbox Code Playgroud)

JSON.stringify发送时需要使用方法将其转换为JSON字符串,

模型绑定器将json数据绑定到您的类对象.

以下代码将正常工作(测试)

$(function () {
    var customer = {contact_name :"Scott",company_name:"HP"};
    $.ajax({
        type: "POST",
        data :JSON.stringify(customer),
        url: "api/Customer",
        contentType: "application/json"
    });
});
Run Code Online (Sandbox Code Playgroud)

结果

在此输入图像描述

contentTypeproperty告诉服务器我们正在以JSON格式发送数据.由于我们发送了JSON数据结构,因此模型绑定将正确发生.

如果检查ajax请求的标头,则可以看到该Content-Type值设置为application/json.

如果未明确指定contentType,它将使用默认的内容类型 application/x-www-form-urlencoded;


2015年11月编辑,以解决评论中提出的其他可能问题

发布复杂对象

假设你有一个复杂的视图模型类作为你的web api动作方法参数

public class CreateUserViewModel
{
   public int Id {set;get;}
   public string Name {set;get;}  
   public List<TagViewModel> Tags {set;get;}
}
public class TagViewModel
{
  public int Id {set;get;}
  public string Code {set;get;}
}
Run Code Online (Sandbox Code Playgroud)

和你的web api终点就像

public class ProductController : Controller
{
    [HttpPost]
    public CreateUserViewModel Save([FromBody] CreateUserViewModel m)
    {
        // I am just returning the posted model as it is. 
        // You may do other stuff and return different response.
        // Ex : missileService.LaunchMissile(m);
        return m;
    }
}
Run Code Online (Sandbox Code Playgroud)

在撰写本文时,ASP.NET MVC 6是最新的稳定版本,在MVC6中,Web api控制器和MVC控制器都继承自基Microsoft.AspNet.Mvc.Controller类.

要从客户端向方法发送数据,下面的代码应该可以正常工作

//Build an object which matches the structure of our view model class
var model = {
    Name: "Shyju",
    Id: 123,
    Tags: [{ Id: 12, Code: "C" }, { Id: 33, Code: "Swift" }]
};

$.ajax({
    type: "POST",
    data: JSON.stringify(model),
    url: "../product/save",
    contentType: "application/json"
}).done(function(res) {       
    console.log('res', res);
    // Do something with the result :)
});
Run Code Online (Sandbox Code Playgroud)

模型绑定适用于某些属性,但不是全部!为什么?

如果不使用[FromBody]属性装饰web api方法参数

[HttpPost]
public CreateUserViewModel Save(CreateUserViewModel m)
{
    return m;
}
Run Code Online (Sandbox Code Playgroud)

并且在不指定contentType属性值的情况下发送模型(原始javascript对象,而不是JSON格式)

$.ajax({
    type: "POST",
    data: model,
    url: "../product/save"
}).done(function (res) {
     console.log('res', res);
});
Run Code Online (Sandbox Code Playgroud)

模型绑定适用于模型上的平面属性,而不适用于类型复杂/其他类型的属性.在我们的例子中,IdName属性将正确绑定到的参数m,但Tags物业将是一个空列表.

如果您使用的是短版本,则会出现同样的问题,$.post该版本在发送请求时将使用默认的Content-Type.

$.post("../product/save", model, function (res) {
    //res contains the markup returned by the partial view
    console.log('res', res);
});
Run Code Online (Sandbox Code Playgroud)

  • 不知道我做了什么,但是我今天早上回来了,回到同一条船上.对象在控制器中为空.在这里,我们再去大声笑 (4认同)

Vai*_*hav 68

在webapi中使用POST可能很棘手!想补充已经正确的答案..

将专注于POST,因为处理GET是微不足道的.我不认为很多人会用webapis解决GET问题.无论如何..

如果您的问题是 - 在MVC Web Api中,如何 - 使用除通用HTTP谓词之外的自定义操作方法名称? - 执行多个帖子? - 发布多个简单类型? - 通过jQuery发布复杂类型?

那么以下解决方案可能有所帮助

首先,要在Web API中使用自定义操作方法,请将web api路由添加为:

public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute(
        name: "ActionApi",
        routeTemplate: "api/{controller}/{action}");
}
Run Code Online (Sandbox Code Playgroud)

然后你可以创建动作方法,如:

[HttpPost]
public string TestMethod([FromBody]string value)
{
    return "Hello from http post web api controller: " + value;
}
Run Code Online (Sandbox Code Playgroud)

现在,从浏览器控制台激发以下jQuery

$.ajax({
    type: 'POST',
    url: 'http://localhost:33649/api/TestApi/TestMethod',
    data: {'':'hello'},
    contentType: 'application/x-www-form-urlencoded',
    dataType: 'json',
    success: function(data){ console.log(data) }
});
Run Code Online (Sandbox Code Playgroud)

第二,要执行多个帖子,很简单,创建多个动作方法并使用[HttpPost] attrib进行装饰.使用[ActionName("MyAction")]分配自定义名称等.将在下面的第四点讨论jQuery

第三,首先,不可能在单个动作中发布多个SIMPLE类型.此外,有一种特殊的格式可以发布一个简单的类型(除了在查询字符串或REST样式中传递参数).这就是让我与Rest Clients(如Fiddler和Chrome的高级REST客户端扩展)一起敲打头脑并在网上狩猎近5个小时的时间点,最终,以下网址被证明是有帮助的.会引用该链接的相关内容可能会变死!

Content-Type: application/x-www-form-urlencoded
in the request header and add a = before the JSON statement:
={"Name":"Turbo Tina","Email":"na@Turbo.Tina"}
Run Code Online (Sandbox Code Playgroud)

PS:注意到特殊的语法

http://forums.asp.net/t/1883467.aspx?The+received+value+is+null+when+I+try+to+Post+to+my+Web+Api

无论如何,让我们克服那个故事.继续:

第四,通过jQuery,ofcourse,$ .ajax()发布复杂类型将立即发挥作用:

我们假设action方法接受一个具有id和name的Person对象.所以,从javascript:

var person = { PersonId:1, Name:"James" }
$.ajax({
    type: 'POST',
    url: 'http://mydomain/api/TestApi/TestMethod',
    data: JSON.stringify(person),
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function(data){ console.log(data) }
});
Run Code Online (Sandbox Code Playgroud)

行动将如下所示:

[HttpPost]
public string TestMethod(Person person)
{
    return "Hello from http post web api controller: " + person.Name;
}
Run Code Online (Sandbox Code Playgroud)

以上所有,为我工作!! 干杯!

  • 我似乎每隔几个月就会遇到这个问题,大部分时间我最终都会解决它,但这次我放弃了。上面的提示都没有为我解决这个问题,所以我决定把它作为一种方法。如果做对这么难,为什么还要麻烦呢?无论如何这只是一种方便 - 只需将内容作为字符串并使用 newtonsoft 对其进行转换。完毕。在尝试了大约一个小时以“简单”的方式解决它之后,大概花了 30 秒的时间来解决它“困难”的方式。我对这种方法并不感冒,但它是否存在根本问题? (3认同)
  • 想补充一点观察.有时,在传递复杂类型(例如:DTO)时,WebAPI端模型绑定失败(null)的原因是模型中的一个或多个属性将不兼容(或无法解析).例如.Guid属性被分配了无效的GUID.在这种情况下,请尝试使用所有对象属性的默认值/空值,然后重试. (2认同)

And*_*ndy 10

我刚刚玩这个并发现了一个相当奇怪的结果.假设您在C#中拥有公共属性,如下所示:

public class Customer
{
    public string contact_name;
    public string company_name;
}
Run Code Online (Sandbox Code Playgroud)

然后你必须按照Shyju的建议做JSON.stringify技巧,并像这样调用它:

var customer = {contact_name :"Scott",company_name:"HP"};
$.ajax({
    type: "POST",
    data :JSON.stringify(customer),
    url: "api/Customer",
    contentType: "application/json"
});
Run Code Online (Sandbox Code Playgroud)

但是,如果您在类上定义getter和setter,如下所示:

public class Customer
{
    public string contact_name { get; set; }
    public string company_name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以更简单地调用它:

$.ajax({
    type: "POST",
    data :customer,
    url: "api/Customer"
});
Run Code Online (Sandbox Code Playgroud)

这使用HTTP标头:

Content-Type:application/x-www-form-urlencoded
Run Code Online (Sandbox Code Playgroud)

我不太确定这里发生了什么,但它看起来像框架中的一个错误(功能?).据推测,不同的绑定方法调用不同的"适配器",而应用程序/ json的适配器使用公共属性,而表单编码数据的适配器则不然.

我不知道哪个被认为是最好的做法.

  • 属性vs字段是它的不同之处.属性是最佳实践.你在第一个例子中称之为属性的是事实上的字段.当你对它们进行get/set时,它们会有一个自动创建的支持字段,使它们成为属性. (6认同)