【问题标题】:How to pass json POST data to Web API method as an object?如何将 json POST 数据作为对象传递给 Web API 方法?
【发布时间】:2013-12-12 03:11:10
【问题描述】:

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

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

如果可能,应该使用 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; }
     }

请求:

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"}

【问题讨论】:

    标签: javascript asp.net-mvc json asp.net-mvc-4 asp.net-web-api


    【解决方案1】:

    使用 JSON.stringify() 获取 JSON 格式的字符串,确保在进行 AJAX 调用时传递以下属性:

    • contentType:'应用程序/json'

    下面是给 ajax 后调用 asp.net web api 的 jquery 代码:

    var product =
        JSON.stringify({
            productGroup: "Fablet",
            productId: 1,
            productName: "Lumia 1525 64 GB",
            sellingPrice: 700
        });
    
    $.ajax({
        URL: 'http://localhost/api/Products',
        type: 'POST',
        contentType: 'application/json',
        data: product,
        success: function (data, status, xhr) {
            alert('Success!');
        },
        error: function (xhr, status, error) {
            alert('Update Error occurred - ' + error);
        }
    });

    【讨论】:

    • dataType 不是必需的。
    【解决方案2】:

    编辑:2017 年 10 月 31 日

    同样的代码/方法也适用于 Asp.Net Core 2.0。主要区别在于,在 asp.net core 中,web api 控制器和 Mvc 控制器都合并为单个控制器模型。因此,您的返回类型可能是 IActionResult 或其实现之一(例如:OkObjectResult


    使用

    contentType:"application/json"
    

    发送时需要使用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"
        });
    });
    

    结果

    contentType 属性告诉服务器我们正在以 JSON 格式发送数据。由于我们发送的是 JSON 数据结构,模型绑定会正常进行。

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

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


    于 2015 年 11 月编辑以解决 cmets 中提出的其他可能问题

    发布复杂对象

    假设您有一个复杂的视图模型类作为您的 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;}
    }
    

    你的 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;
        }
    }
    

    在撰写本文时,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 :)
    });
    

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

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

    [HttpPost]
    public CreateUserViewModel Save(CreateUserViewModel m)
    {
        return m;
    }
    

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

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

    模型绑定将适用于模型上的平面属性,而不是类型为复杂/其他类型的属性。在我们的例子中,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);
    });
    

    【讨论】:

    • 不知道我做了什么,但我今天早上回来了,又回到了同一条船上。控制器中的对象为空。我们又来了,哈哈
    • 确保在使用 fiddler 时内容类型为“Content-Type: application/json”。干杯!
    • 你简直解决了我一天的工作!!!这个小函数“JSON.stringify(data)”成功了!
    • 请记住,如果您这样做(更改 Content-Type 标头)并且您正在发出 CORS 请求,jQuery 将在您的 POST 之前开始添加预检 OPTIONS 请求,服务器必须处理这些请求.
    • 由于复杂类型的问题,我认为只指定 'contentType: 'application/json;' 是一种习惯和 json 将 js 对象字符串化,然后就不需要使用 [FromBody] 属性了。
    【解决方案3】:

    1)在您的客户端,您可以向您发送 http.post 请求,如下所示

    var IndexInfo = JSON.stringify(this.scope.IndexTree);
    this.$http.post('../../../api/EvaluationProcess/InsertEvaluationProcessInputType', "'" + IndexInfo + "'" ).then((response: any) => {}
    

    2)然后在你的 web api 控制器中你可以反序列化它

    public ApiResponce InsertEvaluationProcessInputType([FromBody]string IndexInfo)
        {
    var des = (ApiReceivedListOfObjects<TempDistributedIndex>)Newtonsoft.Json.JsonConvert.DeserializeObject(DecryptedProcessInfo, typeof(ApiReceivedListOfObjects<TempDistributedIndex>));}
    

    3)您的 ApiReceivedListOfObjects 类应如下所示

    public class ApiReceivedListOfObjects<T>
        {
            public List<T> element { get; set; }
    
        }
    

    4)确保您的序列化字符串(此处为 IndexInfo)在步骤 2 中的 JsonConvert.DeserializeObject 命令之前变为如下结构

    var resp = @"
        {
            ""element"": [
            {
                ""A"": ""A Jones"",
                ""B"": ""500015763""
            },
            {
                ""A"": ""B Smith"",
                ""B"": ""504986213""
            },
            {
                ""A"": ""C Brown"",
                ""B"": ""509034361""
            }
            ]
        }";
    

    【讨论】:

      【解决方案4】:

      微软提供了一个很好的例子:

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

      首先验证请求

      if (ModelState.IsValid)
      

      然后使用序列化数据。

      Content = new StringContent(update.Status)
      

      这里的“状态”是复杂类型中的一个字段。序列化由 .NET 完成,无需担心。

      【讨论】:

        【解决方案5】:
        @model MVCClient.Models.ProductDetails
        
        @{
            ViewBag.Title = "ProductDetails";
        }
        <script src="~/Scripts/jquery-1.8.2.min.js"></script>
        <script type="text/javascript">
        
            $(document).ready(function () {
                $("#Save").click(function () {
                    var ProductDetails = new Object();
                    ProductDetails.ProductName =  $("#txt_productName").val();
                    ProductDetails.ProductDetail = $("#txt_desc").val();
                    ProductDetails.Price= $("#txt_price").val();
                    $.ajax({
                        url: "http://localhost:24481/api/Product/addProduct",
                        type: "Post",
                        dataType:'JSON',
                        data:ProductDetails, 
        
                        success: function (data) {
                            alert('Updated Successfully');
                            //window.location.href = "../Index";
                        },
                        error: function (msg) { alert(msg); }
                    });
                });
            });
            </script>
        <h2>ProductDetails</h2>
        
        <form id="form1" method="post">
            <fieldset>
                <legend>ProductDetails</legend>
        
        
                <div class="editor-label">
                    @Html.LabelFor(model => model.ProductName)
                </div>
                <div class="editor-field">
        
                    <input id="txt_productName" type="text" name="fname">
                    @Html.ValidationMessageFor(model => model.ProductName)
                </div>
        
                <div class="editor-label">
                    @Html.LabelFor(model => model.ProductDetail)
                </div>
                <div class="editor-field">
        
                    <input id="txt_desc" type="text" name="fname">
                    @Html.ValidationMessageFor(model => model.ProductDetail)
                </div>
        
                <div class="editor-label">
                    @Html.LabelFor(model => model.Price)
                </div>
                <div class="editor-field">
        
                    <input id="txt_price" type="text" name="fname">
                    @Html.ValidationMessageFor(model => model.Price)
                </div>
        
        
        
                <p>
                    <input id="Save" type="button" value="Create" />
                </p>
            </fieldset>
        
        </form>
            <div>
                @Html.ActionLink("Back to List", "Index")
            </div>
        
        </form>
        
        
        
        @section Scripts {
            @Scripts.Render("~/bundles/jqueryval")
        }
        

        【讨论】:

          【解决方案6】:

          以下代码以 json 格式返回数据,而不是 xml -Web API 2:-

          在 Global.asax 文件中加入下面一行

          GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                  GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
          

          【讨论】:

            【解决方案7】:

            确保您的 WebAPI 服务需要一个结构与您传递的 JSON 匹配的强类型对象。并确保将要发布的 JSON 字符串化。

            这是我的 JavaScript(使用 AngluarJS):

            $scope.updateUserActivity = function (_objuserActivity) {
                    $http
                    ({
                        method: 'post',
                        url: 'your url here',
                        headers: { 'Content-Type': 'application/json'},
                        data: JSON.stringify(_objuserActivity)
                    })
                    .then(function (response)
                    {
                        alert("success");
                    })
                    .catch(function (response)
                    {
                        alert("failure");
                    })
                    .finally(function ()
                    {
                    });
            

            这是我的 WebAPI 控制器:

            [HttpPost]
            [AcceptVerbs("POST")]
            public string POSTMe([FromBody]Models.UserActivity _activity)
            {
                return "hello";
            }
            

            【讨论】:

              【解决方案8】:

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

              public class Customer
              {
                  public string contact_name;
                  public string company_name;
              }
              

              那么您必须按照 Shyju 的建议执行 JSON.stringify 技巧并这样称呼它:

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

              但是,如果您像这样在类中定义 getter 和 setter:

              public class Customer
              {
                  public string contact_name { get; set; }
                  public string company_name { get; set; }
              }
              

              那么你可以更简单地调用它:

              $.ajax({
                  type: "POST",
                  data :customer,
                  url: "api/Customer"
              });
              

              这使用 HTTP 标头:

              Content-Type:application/x-www-form-urlencoded
              

              我不太确定这里发生了什么,但它看起来像是框架中的一个错误(功能?)。据推测,不同的绑定方法正在调用不同的“适配器”,而用于 application/json 的适配器适用于公共属性,而用于表单编码数据的适配器则不能。

              不过,我不知道哪种做法会被认为是最佳做法。

              【讨论】:

              • 属性与字段是它不同的原因。属性是最佳实践。在第一个示例中,您所谓的属性实际上是字段。当您在它们上设置 get/set 时,它们就会自动创建一个支持字段,使它们成为属性。
              • 这是真的,也很奇怪。只有字段的普通类不会绑定到表单帖子,但属性会。顺便说一句:仍然没有解释为什么会这样......?我只能猜测内部逻辑只会将JSON数据绑定到字段,并形成post数据到属性,就是这样......?
              • 之所以如此,是因为代码只查找属性。由于使用公共字段不是最佳实践,MS 团队决定不允许 not 最佳实践场景,恕我直言,这是一个很好的理由。
              【解决方案9】:

              在 webapi 中使用 POST 可能会很棘手! 想添加到已经正确的答案..

              将特别关注 POST,因为处理 GET 是微不足道的。我不认为很多人会四处寻找解决 GET 与 webapis 的问题。反正..

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

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

              首先,要在 Web API 中使用 自定义 Action Methods,添加一个 web api 路由:

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

              然后您可以创建如下操作方法:

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

              现在,从浏览器控制台触发以下 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) }
              });
              

              第二,执行多个帖子,很简单,创建多个动作方法并用[HttpPost]属性装饰。使用[ActionName("MyAction")] 来分配自定义名称等。下面第四点会来到jQuery

              第三,首先,在一个操作中发布多个简单类型是不可能的。 此外,还有一个特殊格式可以发布一个单一的简单类型(除了在查询字符串或REST风格中传递参数)。 这就是让我与 Rest Clients(如 Fiddler 和 Chrome 的 Advanced REST 客户端扩展)并在网上搜寻近 5 个小时的原因,最终,以下 URL 被证明是有帮助的。会引用相关内容的链接可能会死机!

              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"}
              

              PS:注意到特殊语法

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

              不管怎样,让我们​​结束这个故事。继续:

              第四,通过 jQuery 发布复杂类型,当然,$.ajax() 很快就会发挥作用:

              假设 action 方法接受一个 Person 对象,它有一个 id 和一个名字。所以,从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) }
              });
              

              动作如下:

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

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

              【讨论】:

              • 我似乎每隔几个月就会遇到这个问题,大部分时间我最终都会解决它,但这一次我已经放弃了。上面的提示都没有为我解决这个问题,所以我决定将其作为一种方法。如果很难做到正确,那何必呢?无论如何,这只是一种方便 - 只需将内容作为字符串输入并使用 newtonsoft 对其进行转换。完毕。在尝试了大约一个小时以“简单”的方式解决它之后,大概花了 30 秒来解决它。我对这种方法并不感兴趣,但它有根本问题吗?
              • PS:在WebApi2中,我们现在可以使用路由装饰器了。所以这个问题主要是解决的。 asp.net/web-api/overview/web-api-routing-and-actions/…
              • 想添加一个观察。有时,在传递复杂类型(例如:DTO)时,WebAPI 端的模型绑定失败(null)的原因是模型中的一个或多个属性将不兼容(或无法解析)。例如。为 Guid 属性分配了无效的 GUID。在这种情况下,请尝试对所有对象属性使用默认/空值,然后重试。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-05-12
              • 2023-03-03
              • 2019-10-21
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多