web-api POST正文对象始终为null

Inn*_*Dan 62 c# rest asp.net-web-api

我还在学习网络API,所以请原谅我,如果我的问题听起来很愚蠢.

我有这个StudentController:

public HttpResponseMessage PostStudent([FromBody]Models.Student student)
        {
            if (DBManager.createStudent(student) != null)
                return Request.CreateResponse(HttpStatusCode.Created, student);
            else
                return Request.CreateResponse(HttpStatusCode.BadRequest, student);
        }
Run Code Online (Sandbox Code Playgroud)

为了测试这是否有效,我使用Google Chrome的扩展程序"Postman"来构建HTTP POST请求以对其进行测试.

这是我的原始POST请求:

POST /api/Student HTTP/1.1
Host: localhost:1118
Content-Type: application/json
Cache-Control: no-cache

{"student": [{"name":"John Doe", "age":18, "country":"United States of America"}]}
Run Code Online (Sandbox Code Playgroud)

"student"应该是一个对象,但是当我调试应用程序时,API接收学生对象,但内容始终是NULL.

chr*_*rjs 51

我现在正在寻找解决问题的解决方案,所以我将分享我的解决方案.

您的模型需要具有空/默认构造函数,否则显然无法创建模型.重构时要小心.;)

  • @AndreiDragotoniu我认为当你创建了一个需要参数的构造函数时就会出现问题。如果您这样做了,这将覆盖默认构造函数。在这种情况下,您还需要创建一个额外的空构造函数。 (3认同)
  • 这是应该添加到接受的答案中的重要信息。 (2认同)

Raj*_*dar 50

FromBody是一个奇怪的属性,因为当输入POST值不是基本类型时,输入POST值必须是特定格式,以使参数非空.(这里的学生)

  1. {"name":"John Doe", "age":18, "country":"United States of America"}以json的身份尝试您的请求.
  2. 删除该[FromBody]属性并尝试解决方案.它应该适用于非原始类型.(学生)
  3. 使用该[FromBody]属性,另一个选项是以=Value格式而不是key=value格式发送值.这意味着你的关键值student应该是一个空字符串......

还有其他选项可以为学生类编写自定义模型绑定器,并使用自定义绑定器对参数进行属性设置.

  • 对于其他读者..这里的原始问题是OP正在发送一个包含学生属性的JSON对象,而不是像webapi期望的那样只发送学生对象. (11认同)
  • 删除了 [FromBody],仍然无法正常工作。学生对象的内容仍然为空..(例如姓名=空,国家=空,年龄=空) (3认同)
  • 这是一个迟到的评论,但你的解决方案帮助我解决了我在过去两个小时内遇到的问题.干杯:) (3认同)

Mil*_*vec 30

我花了几个小时来处理这个问题... :(在POST参数对象声明中需要获取getter和setter.我不建议使用简单的数据对象(string,int,...),因为它们需要特殊的请求格式.

[HttpPost]
public HttpResponseMessage PostProcedure(EdiconLogFilter filter){
...
}
Run Code Online (Sandbox Code Playgroud)

在以下时间不起作用:

public class EdiconLogFilter
{
    public string fClientName;
    public string fUserName;
    public string fMinutes;
    public string fLogDate;
}
Run Code Online (Sandbox Code Playgroud)

工作正常时:

public class EdiconLogFilter
{
    public string fClientName { get; set; }
    public string fUserName { get; set; }
    public string fMinutes { get; set; }
    public string fLogDate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

  • 哦,哇,我不敢相信这实际上是我的问题...非常感谢。 (4认同)

Jos*_*cid 13

这是一个有点旧的,我的答案将归结为最后一个地方,但即便如此,我想分享我的经验.

尝试了每个建议,但仍然在PUT [FromBody]中具有相同的"null"值.

终于发现它是关于Date格式的,而JSON序列化我的Angular对象的EndDate属性.

没有抛出任何错误,只是收到一个空的FromBody对象....

  • 我一直在向它传递各种奇怪的数据,期望它在它的映射中很聪明,但是没有......它会死掉并且除了null之外什么都没有.我再次尝试了表现良好的数据和嘿presto,排序.你的答案鼓励我这样做! (3认同)
  • 这个答案非常有用,因为日期格式化也是我遇到的错误的原因. (2认同)

Ray*_*ega 13

如果请求的JSON对象的任何值与服务所期望的类型不同,那么[FromBody]参数将是null.

例如,如果json中的age属性具有float值:

"时代":18.0

但API服务期望它是一个 int

"时代":18

然后studentnull.(除非没有空引用检查,否则不会在响应中发送错误消息).


Joh*_*n M 8

如果使用Postman,请确保:

  • 您已将"Content-Type"标头设置为"application/json"
  • 你发送的身体是"原始的"
  • 如果您使用[FromBody],则无需在任何位置指定参数名称

我愚蠢地试图将我的JSON作为表单数据发送,呃......


Phu*_*huo 8

将 TRACING 添加到 json 序列化程序会很有帮助,这样您就可以在出现问题时看到发生了什么。

定义一个 ITraceWriter 实现来显示其调试输出,例如:

class TraceWriter : Newtonsoft.Json.Serialization.ITraceWriter
{
    public TraceLevel LevelFilter {
        get {
            return TraceLevel.Error;
        }
    }

    public void Trace(TraceLevel level, string message, Exception ex)
    {
        Console.WriteLine("JSON {0} {1}: {2}", level, message, ex);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在您的 WebApiConfig 中执行以下操作:

    config.Formatters.JsonFormatter.SerializerSettings.TraceWriter = new TraceWriter();
Run Code Online (Sandbox Code Playgroud)

(也许将其包装在 #if DEBUG 中)


小智 6

我也尝试使用 [FromBody],但是,我尝试填充一个字符串变量,因为输入会发生变化,我只需要将它传递给后端服务,但这始终为空

Post([FromBody]string Input]) 
Run Code Online (Sandbox Code Playgroud)

所以我更改了方法签名以使用动态类,然后将其转换为字符串

Post(dynamic DynamicClass)
{
   string Input = JsonConvert.SerializeObject(DynamicClass);
Run Code Online (Sandbox Code Playgroud)

这效果很好。


Flo*_*ter 6

TL; DR:不要使用[FromBody],而是使用更好的错误处理来滚动自己的版本.原因如下.

其他答案描述了此问题的许多可能原因.然而,根本原因在于[FromBody]简单地进行了可怕的错误处理,这使得它在生产代码中几乎无用.

例如,参数的最典型原因之一null是请求主体具有无效语法(例如,无效的JSON).在这种情况下,合理的API将返回400 BAD REQUEST,合理的Web框架将自动执行此操作.但是,ASP.NET Web API在这方面并不合理.它只是将参数设置为null,然后请求处理程序需要"手动"代码来检查参数是否null.

因此,这里给出的许多答案在错误处理方面是不完整的,并且错误或恶意客户端可能通过发送无效请求在服务器端引起意外行为,这将(在最好的情况下)抛出NullReferenceException某个地方并返回错误的状态500 INTERNAL SERVER ERROR,或者更糟糕,做一些意外或崩溃或暴露安全漏洞.

一个合适的解决方案是编写一个自定义" [FromBody]"属性,该属性可以正确处理错误并返回正确的状态代码,理想情况下可以使用一些诊断信息来帮助客户端开发

可能有帮助(尚未测试)的解决方案是制作所需的参数,如下所示:https://stackoverflow.com/a/19322688/2279059

以下笨拙的解决方案也有效:

// BAD EXAMPLE, but may work reasonably well for "internal" APIs...

public HttpResponseMessage MyAction([FromBody] JObject json)
{
  // Check if JSON from request body has been parsed correctly
  if (json == null) {
    var response = new HttpResponseMessage(HttpStatusCode.BadRequest) {
      ReasonPhrase = "Invalid JSON"
    };
    throw new HttpResponseException(response);
  }

  MyParameterModel param;
  try {
    param = json.ToObject<MyParameterModel>();
  }
  catch (JsonException e) {
    var response = new HttpResponseMessage(HttpStatusCode.BadRequest) {
      ReasonPhrase = String.Format("Invalid parameter: {0}", e.Message)
    };
    throw new HttpResponseException(response);
  }

  // ... Request handling goes here ...

}
Run Code Online (Sandbox Code Playgroud)

这确实(希望)正确的错误处理,但声明较少.例如,如果您使用Swagger来记录您的API,它将不知道参数类型,这意味着您需要找到一些手动变通方法来记录您的参数.这只是为了说明[FromBody]应该做什么.

编辑:一个不太笨拙的解决方案是检查ModelState:https://stackoverflow.com/a/38515689/2279059

编辑:如果使用with 并且缺少参数ModelState.IsValid,它似乎不会像人们期望的那样设置.所以这也没用.falseJsonPropertyRequired = Required.Always

但是,在我看来,任何需要在每个请求处理程序中编写附加代码的解决方案都是不可接受的.在像.NET这样的语言中,具有强大的序列化功能,并且在像ASP.NET Web API这样的框架中,请求验证应该是自动的和内置的,并且它是完全可行的,即使Microsoft没有提供必要的内置工具.