Post参数始终为null

问题描述 投票:189回答:36

自从升级到RC for WebAPI后,我在WebAPI上调用POST时遇到了一些奇怪的问题。我甚至回到了新项目生成的基本版本。所以:

public void Post(string value)
{
}

并从Fiddler打来电话:

Header:
User-Agent: Fiddler
Host: localhost:60725
Content-Type: application/json
Content-Length: 29

Body:
{
    "value": "test"
}

当我调试时,字符串“value”永远不会被分配给。它总是为NULL。谁有这个问题?

(我第一次看到更复杂类型的问题)

问题不仅仅是绑定到ASP.NET MVC 4,在安装RC后,新的ASP.NET MVC 3项目也会出现同样的问题

c# .net asp.net-web-api asp.net-web-api-routing
36个回答
99
投票

由于您只有一个参数,您可以尝试使用[FromBody]属性进行装饰,或者更改方法以接受具有值作为属性的DTO,如我在此建议的那样:MVC4 RC WebApi parameter binding

更新:官方的ASP.NET站点今天更新了一个很好的解释:https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-1

简而言之,当在正文中发送单个简单类型时,只发送前缀为等号(=)的值,例如:身体:

=test


6
投票

对于那些像我一样与Swagger或Postman有同样问题的人,如果你在帖子中传递一个简单的属性作为字符串,即使指定了“ContentType”,你仍然会得到一个空值。

通过:

myvalue的

将控制器中的null作为null。

但如果你通过:

“myvalue的”

价值将是正确的。

引号在这里有所不同。当然,这只适用于Swagger和Postman。例如,在使用Angular的前端应用程序中,这应该由框架自动解决。


5
投票

我有同样的问题,发现当将内容类型更改为“application / json”时没有解决问题。但是“application / json; charset = utf-8”起作用了。


4
投票

我有一个类似的问题,我的Web API方法的请求对象始终为null。我注意到,由于控制器操作名称以“Get”为前缀,因此Web API将其视为HTTP GET而不是POST。重命名控制器操作后,它现在按预期工作。


3
投票

在我的情况下,问题是参数是一个字符串而不是一个对象,我将参数更改为Newsoft.Json的JObject,它的工作原理。


2
投票

添加线

        ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());

在Global.asax.cs中,函数protected void Application_Start()的结尾在ASP.NET MVC3中修复了类似的问题。


2
投票

使用Angular,我能够以这种格式传递数据:

 data: '=' + JSON.stringify({ u: $scope.usrname1, p: $scope.pwd1 }),
 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' }

并在Web API控制器中:

    [HttpPost]
    public Hashtable Post([FromBody]string jsonString)
    {
        IDictionary<string, string> data = JsonConvert.DeserializeObject<IDictionary<string, string>>(jsonString);
        string username = data["u"];
        string pwd = data["p"];
   ......

或者,我也可以发布这样的JSON数据:

    data: { PaintingId: 1, Title: "Animal show", Price: 10.50 } 

并且,在控制器中,接受这样的类类型:

    [HttpPost]
    public string POST(Models.PostModel pm)
    {

     ....
    }

无论哪种方式都有效,如果您在API中有已建立的公共类,则发布JSON,否则发布'='+ JSON.stringify({..:...,..:...})


2
投票

如果您正在为Xml Formatter或JSON Formatter使用DataContractSerializer,则需要摆脱它。我在我的WebApiConfig文件中有这个:

public static void Register(HttpConfiguration config)
{
     config.Routes.MapHttpRoute(
           name: "DefaultApi",
           routeTemplate: "api/{controller}/{id}",
           defaults: new { id = RouteParameter.Optional }
     );    

     var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
     jsonFormatter.UseDataContractJsonSerializer = true;
}

我只是注释掉jsonFormatter.UseDataContractJsonSerializer = true;and我的输入参数不再是null。感谢'Despertar'给我一个线索。


2
投票

如果您确定已发送的JSON,那么您必须仔细跟踪您的API:

  1. 安装Microsoft.AspNet.WebApi.Tracing
  2. config.EnableSystemDiagnosticsTracing();方法中的WebApiConfig类中添加Register

现在看一下Debug输出,你可能会发现一个无效的ModelState日志条目。

如果ModelState无效,你可以在其Errors找到真正的原因:

没有人能猜到这样的例外:

Could not load file or assembly 'Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

1
投票

我知道这不是这个问题的答案,但我在寻找问题的解决方案时遇到了它。

在我的情况下,复杂类型没有绑定但我没有做POST,我正在使用查询字符串参数进行GET。解决方案是将[FromUri]添加到arg:

public class MyController : ApiController
{
    public IEnumerable<MyModel> Get([FromUri] MyComplexType input)
    {
        // input is not null as long as [FromUri] is present in the method arg
    }
}

1
投票

我在Fiddler遇到了同样的问题。我已经在请求标题中有Content-Type: application/json; charset=utf-8Content-Type: application/json

我的请求体也是一个简单的字符串,在Fiddler我写过:{'controller':'ctrl'}。这使我的POST方法中的字符串参数为null

修复:记得使用引号,从而指示一个字符串。也就是说,我通过编写"{'controller':'ctrl'}"来修复它。 (注意:在编写JSON时,要么确保使用撇号,要么转义引号,如下所示:"{\"controller\":\"ctrl\"}")。


100
投票

今天我一直在摸不着头脑。

我的解决方案是将[FromBody]更改为HttpRequestMessage,从根本上提升HTTP堆栈。

在我的情况下,我通过电线发送数据,这是拉链json,然后base64'd。这一切都来自Android应用程序。

我的Web端点的原始签名看起来像这样(使用[FromBody]):

我对此问题的解决方法是恢复使用HttpRequestMessage作为我的端点的签名。

然后,您可以使用以下代码行访问帖子数据:

这有效,并允许您访问原始未触动的后期数据。你不必乱搞fiddler在你的字符串的开头添加一个=符号或更改内容类型。

顺便说一句,我首先尝试按照上面的一个答案将内容类型更改为:“Content-Type:application / x-www-form-urlencoded”。对于原始数据,这是一个糟糕的建议,因为它剥离了+字符。

所以像这样开始的base64字符串:“MQ0AAB + LCAAAAAA”就像这样的“MQ0AAB LCAAAAAA”!不是你想要的。

使用HttpRequestMessage的另一个好处是,您可以访问端点中的所有http标头。


1
投票

我发现处理简单JSON对象的最简单方法是传递给MVC 6,它获取了像NewtonSoft jObject这样的post参数的类型:

public ActionResult Test2([FromBody] jObject str)
{
        return Json(new { message = "Test1 Returned: "+ str }); ;
}

1
投票

对我来说最好的解决方案是使用完整的HTTP,如下所示:

[Route("api/open")]
[HttpPost]
public async Task<string> open(HttpRequestMessage request)
{
    var json = await request.Content.ReadAsStringAsync();
    JavaScriptSerializer jss = new JavaScriptSerializer();            
    WS_OpenSession param = jss.Deserialize<WS_OpenSession>(json);
    return param.sessionid;
}

然后将字符串反序列化为您希望在帖子正文中的对象。对我来说,WS_OpenSession是一个包含sessionid,user和key的类。

您可以从那里使用param对象并访问其属性。

非常非常有效。

我确实说过来自这个网址:

http://bizcoder.com/posting-raw-json-to-web-api


1
投票

对于复杂类型,Web API尝试使用媒体类型格式化程序从邮件正文中读取值。

请检查您是否有任何[Serializable]属性装饰您的模型类。

删除该属性以查看它是否有效。这对我有用。


1
投票

我参加派对的时间有点晚了,但是在使用控制器时偶然发现NULL值的人只需在POST请求的前面加上“=”。

当我使用application / json Content-Type时,控制器也传递了NULL值。请注意下面的“application / x-www-form-urlencoded”内容类型。然而,API的返回类型是“application / json”。

 public static string HttpPostRequest(string url, Dictionary<string, string> postParameters)
    {
        string postData = "=";

        foreach (string key in postParameters.Keys)
        {
            postData += HttpUtility.UrlEncode(key) + "="
                  + HttpUtility.UrlEncode(postParameters[key]) + ",";
        }

        HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
        myHttpWebRequest.Method = "POST";

        byte[] data = System.Text.Encoding.ASCII.GetBytes(postData);

        myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
        myHttpWebRequest.ContentLength = data.Length;

        Stream requestStream = myHttpWebRequest.GetRequestStream();
        requestStream.Write(data, 0, data.Length);
        requestStream.Close();

        HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();

        Stream responseStream = myHttpWebResponse.GetResponseStream();

        StreamReader myStreamReader = new StreamReader(responseStream, System.Text.Encoding.Default);

        string pageContent = myStreamReader.ReadToEnd();

        myStreamReader.Close();
        responseStream.Close();

        myHttpWebResponse.Close();

        return pageContent;
    }

1
投票

你希望发布什么类型的值无关紧要,只需将其括在引号内,即可将其作为字符串。不适用于复杂类型。

JavaScript的:

    var myData = null, url = 'api/' + 'Named/' + 'NamedMethod';

    myData = 7;

    $http.post(url, "'" + myData + "'")
         .then(function (response) { console.log(response.data); });

    myData = "some sentence";

    $http.post(url, "'" + myData + "'")
         .then(function (response) { console.log(response.data); });

    myData = { name: 'person name', age: 21 };

    $http.post(url, "'" + JSON.stringify(myData) + "'")
         .then(function (response) { console.log(response.data); });

    $http.post(url, "'" + angular.toJson(myData) + "'")
         .then(function (response) { console.log(response.data); });

C#:

    public class NamedController : ApiController
    {
        [HttpPost]
        public int NamedMethod([FromBody] string value)
        {
            return value == null ? 1 : 0;
        }
    }

1
投票

仔细检查您的数据类型。 dotnet模型绑定器不会将float转换为整数(我假设其他相关概念)。这将导致整个模型被拒绝。

如果你有这样的json:

{
    "shoeSize": 10.5
}

但你的c#模型看起来像这样:

class Shoe{
    public int shoeSize;
}

模型绑定器将拒绝模型,您将获得null。


1
投票

我有同样的问题,将null作为参数,但它与大对象有关。原来这个问题与IIS最大长度有关。它可以在web.config中配置。

  <system.web>
    <httpRuntime targetFramework="4.7" maxRequestLength="1073741824" />
  </system.web>

我想知道为什么Web API会抑制错误并将空对象发送到我的API。我使用Microsoft.AspNet.WebApi.Tracing发现了错误。


1
投票

我已经很晚了,但是遇到了类似的问题,经过一天的大量解答并获得背景后,我发现了最简单/轻量级的解决方案,将一个或多个参数传回Web API 2操作是如下:

这假定您知道如何使用正确的路由设置Web API控制器/操作,如果不是,请参阅:https://docs.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api

首先是Controller Action,这个解决方案还需要Newtonsoft.Json库。

[HttpPost]
public string PostProcessData([FromBody]string parameters) {
    if (!String.IsNullOrEmpty(parameters)) {
        JObject json = JObject.Parse(parameters);

        // Code logic below
        // Can access params via json["paramName"].ToString();
    }
    return "";
}

客户端使用jQuery

var dataToSend = JSON.stringify({ param1: "value1", param2: "value2"...});
$.post('/Web_API_URI', { '': dataToSend }).done(function (data) {
     console.debug(data); // returned data from Web API
 });

我发现的关键问题是确保只将一个整体参数发送回Web API,并确保它没有名称,只有值{ '': dataToSend }otherwise,您的值在服务器端将为null。

通过这种方式,您可以在JSON结构中向Web API发送一个或多个参数,而无需在服务器端声明任何额外的对象来处理复杂数据。 JObject还允许您动态迭代传入的所有参数,以便在参数随时间变化时实现更轻松的可伸缩性。希望这可以帮助那些像我一样苦苦挣扎的人。


0
投票

正确将正文中的单个参数传递给Web API可以使用此代码$.post(url, { '': productId }

并在行动中抓住它[HttpPost] public ShoppingCartAddRemoveViewModel Delete([FromBody]string value)

关键是使用魔术词'价值'。它也可以是int或某种原始类型。无论内容类型还是标题更正,Mess都认为此代码在mvc post action中不起作用。


0
投票

问题是你的action方法期望一个简单的类型,即字符串参数值。你提供的是一个对象。

您的问题有两种解决方案。

  1. 创建一个带有“value”属性的简单类,然后使用该类作为参数,在这种情况下,Web API模型绑定将从请求中读取JSON对象并将其绑定到您的param对象“values”属性。
  2. 只需传递字符串值“test”,它就能正常工作。

70
投票

我刚刚使用Fiddler发生了这种情况。问题是我没有指定Content-Type

尝试在POST请求中包含Content-Type的标头。

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

或者,根据下面的评论,您可能需要包含JSON标头

Content-Type: application/json

0
投票

如果您放置[FromBody]注释并且您将Dto对象作为方法的参数并且仍然无法获取数据,请开始查看DTO的属性和字段。

我有同样的问题,我的DTO无效。我发现原因之一是其中一个属性指向一个无法序列化的对象:(这会导致media-formatter无法解析数据。因此该对象始终为null。希望它也可以帮助其他人


56
投票

我也遇到过这个问题,这就是我解决问题的方法

webapi代码:

public void Post([FromBody] dynamic data)
{
    string value = data.value;
    /* do stuff */
}

客户代码:

$.post( "webapi/address", { value: "some value" } );

35
投票

我正在使用Postman,我也犯了同样的错误..将value作为json对象而不是字符串传递

{
    "value": "test"
}

显然,当api参数是string类型时,上面的错误。

所以,只需在api体中用双引号传递字符串:

"test"

18
投票

尝试创建一个类作为数据模型,然后发送一个JSON对象,其属性与数据模型类的属性相匹配。 (注意:我已对此进行了测试,它与我刚刚下载的最新MVC 4 RC 2012一起使用)。

public HttpResponseMessage Post(ValueModel model)
{
    return Request.CreateResponse<string>(HttpStatusCode.OK, "Value Recieved: " + model.Value);
}

public class ValueModel
{
    public string Value { get; set; }
}

下面的JSON对象在HTTP-POST主体中发送,content-type是application / json

{ "value": "In MVC4 Beta you could map to simple types like string, but testing with RC 2012 I have only been able to map to DataModels and only JSON (application/json) and url-encoded (application/x-www-form-urlencoded body formats have worked. XML is not working for some reason" }

我相信您必须创建数据模型类的原因是因为假设简单值来自url参数,并且假设单个复杂值来自正文。他们确实有[FromBody][FromUrl]属性,但使用[FromBody] string value仍然不适合我。看起来他们仍然在制造很多错误,所以我相信这将在未来发生变化。

编辑:让XML在正文中工作。默认的XML序列化程序已更改为DataContractSerializer而不是XmlSerializer。在我的Global.asax文件中放入以下行修复了此问题(reference

GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;

13
投票

经过一些尝试,我认为默认行为是正确的,没有什么可以破解。

唯一的技巧是:如果您的post方法参数是string,如下所示,您应该在正文中发送一个带双引号的纯字符串(当使用ajax或postman时),例如,

//send "{\"a\":1}" in body to me, note the outer double quotes
[HttpPost("api1")]
public String PostMethod1([FromBody]string value)
{
    return "received " + value; //  "received {\"a\":1}"
}

否则,如果你在帖子体中发送一个没有外部双引号和转义内部引号的json字符串,那么它应该能够被解析为模型类(参数类型),例如,{"a":1, "b":2}

public class MyPoco{
    public int a;
    public int b;
}

//send {"a":1, "b":2} in body to me
[HttpPost("api2")]
public String PostMethod2([FromBody]MyPoco value)
{
    return "received " + value.ToString();  //"received your_namespace+MyPoco"
}

11
投票

我现在正在寻找这个问题的解决方案几分钟,所以我将分享我的解决方案。

如果发布模型,模型需要有一个空/默认构造函数,否则显然无法创建模型。重构时要小心。 ;)


6
投票

这对我有用:

  1. 创建一个C#DTO类,其中包含要从jQuery / Ajax传递的每个属性的属性 public class EntityData { public string Attr1 { get; set; } public string Attr2 { get; set; } }
  2. 定义web api方法: [HttpPost()] public JObject AddNewEntity([FromBody] EntityData entityData) {
  3. 像这样调用web api: var entityData = { "attr1": "value1", "attr2": "value2" }; $.ajax({ type: "POST", url: "/api/YOURCONTROLLER/addnewentity", async: true, cache: false, data: JSON.stringify(entityData), contentType: "application/json; charset=utf-8", dataType: "json", success: function (response) { ... } });
© www.soinside.com 2019 - 2024. All rights reserved.