Douglas Gaskell
Douglas Gaskell

Reputation: 10100

ASP.NET Core API POST parameter is always null

I have read the following:

My endpoint:

[HttpPost]
[Route("/getter/validatecookie")]
public async Task<IActionResult> GetRankings([FromBody] string cookie)
{
    int world = 5;
    ApiGetter getter = new ApiGetter(_config, cookie);
    if (!await IsValidCookie(getter, world))
    {
        return BadRequest("Invalid CotG Session");
    }
    HttpContext.Session.SetString("cotgCookie", cookie);
    return Ok();
}

My request:

$http.post(ENDPOINTS["Validate Cookie"],  cookie , {'Content-Type': 'application/json'});

Where cookie is the a string I am sending from the user input.

The request posts to the endpoint with the appropriate data. However, my string is always null. I have tried removing the [FromBody] tag, as well as adding a = in front of the posted data with no luck. I have also tried adding and removing different content types with all combinations of the above.

The reason why I am doing this specific action is long and does not matter for this question.

Why is my parameter always null no matter what I seem to do?

Edit: I have also tried using {cookie: cookie}

Edit2: The request:

Request URL:http://localhost:54093/getter/validatecookie
Request Method:POST
Status Code:400 Bad Request
Remote Address:[::1]:54093

Response Headers

Content-Type:text/plain; charset=utf-8
Date:Mon, 23 Jan 2017 03:12:54 GMT
Server:Kestrel
Transfer-Encoding:chunked
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?QzpcVXNlcnNcRG91Z2xhc2cxNGJcRG9jdW1lbnRzXFByb2dyYW1taW5nXENvdEdcQ290RyBBcHBcc3JjXENvdEdcZ2V0dGVyXHZhbGlkYXRlY29va2ll?=

Request Headers

POST /getter/validatecookie HTTP/1.1
Host: localhost:54093
Connection: keep-alive
Content-Length: 221
Accept: application/json, text/plain, */*
Origin: http://localhost:54093
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://localhost:54093/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

Request Payload

=sec_session_id=[redacted]; _ga=[redacted]; AWSELB=[redacted]

Upvotes: 44

Views: 72235

Answers (10)

Mario Levesque
Mario Levesque

Reputation: 1037

Using [FromForm] on both parameters fixed it for me. As in :

[HttpPost]
public IActionResult Insrtmyinfo([FromForm] string firstparam, [FromForm] string secndparam)

...

Upvotes: 0

Unkreaper
Unkreaper

Reputation: 25

In my case, I had it setup like @guhyeon suggested, with a model class containing only a string property. Except, it wasn't defined as a property, it was simply

public class PdfInfoRequestDto
{
    public string PdfInBase64;
}

And for some reason, this worked for a .net framework 4.7.2 webapi and I could receive the value I needed from the request body. But when I tried replicating the same in a .net core 3.1 webapi, with the same request, models and everything, the value in PdfInBase64 always arrived in my controller as null. After changing the field to a property:

public string PdfInBase64 { get; set; }

I started correctly getting the value that was being passed in the request body.

Upvotes: 1

Shadi Alnamrouti
Shadi Alnamrouti

Reputation: 13318

For me, just adding [FromBody] to the parameters list solved the problem.

May this save someone's time.

Upvotes: 4

contactmatt
contactmatt

Reputation: 18630

My particular issue was that the model binding was silently failing for a JSON model. (It was always null).

As I had the exact JSON being posted, I was able to debug it locally by running the web-service locally, and posting to my controller via cURL (can use POSTMAN).

Using the below code, I was able to see the exact exception occurring during serialization.

    [HttpPost]
    public IActionResult MyAction([FromBody] dynamic request)
    {            
        if (request != null)
        {
            try
            {
                var objAttempt =
                    JsonConvert.DeserializeObject<MyModel>(
                        JsonConvert.SerializeObject(request));
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
                throw;
            }
        }

Upvotes: 0

Danno
Danno

Reputation: 1003

I struggled with this for far to long and finally, after looking at what a DevExpress control was doing for "PUT"ting to a Razor Page, I discovered this nugget:

JavaScript

$.ajax({
        type: "PUT",
        url: "/GoalGrid?handler=Single",
        dataType: "json",
        contentType: "application/x-www-form-urlencoded; charset=UTF-8",
        data: {values: single }
    })

GoalGrid.cshtml.cs

public JsonResult OnPutSingle(string values)
{ 
     // Do stuff with 'values' here 
}

The trick is to use the "application/x-www-form-urlencoded; charset=UTF-8" as your contentType for the request. This way you don't need to create a class for a single string value. And things work as expected.

Upvotes: 3

Demir
Demir

Reputation: 1837

I needed to post string data by .Net Desktop Client to .NET Core host. I was getting unsupported media error. I have followed Shaun Luttin's answer and worked fine. The I found something easier to get just string data as folows in case someone else finds useful:

[HttpPost]
[Route("Request/Echo")]
public async Task<string> Echo()
{
    using (var Reader = new StreamReader(Request.Body, Encoding.UTF8))
    {
        return await Reader.ReadToEndAsync();
    }
}

This post is very useful.

Upvotes: 1

guhyeon
guhyeon

Reputation: 595

ridiculously, in dot net core you cannot use just "frombody string parameter". you should make a model class for just one string parameter.

public async Task<IActionResult> GetRankings([FromBody] string cookie)

=>

//1. make a model. MyCookie.cs
class MyCookie{
   public string Cookie { get; set; }
}
//2. edit your parameter
public async Task<IActionResult> GetRankings([FromBody] MyCookie cookie)

Upvotes: 9

statler
statler

Reputation: 1381

Shaun Luttin's answer works, but it misses one important piece of information. The reason your string is not recognised is because it is not a JSON string.

Do this;

var payload=JSON.stringify("=sec_session_id=[redacted]; _ga=[redacted]; AWSELB=[redacted]");

Then you can leave the controller as it is;

$.ajax({
    url: http://localhost:54093/getter/validatecookie,
    type: 'POST',
    contentType: 'application/json',
    data: payload
});

It is embarassing how long this took me to figure out. I really hope it helps someone!

Upvotes: 11

Shaun Luttin
Shaun Luttin

Reputation: 141712

The problem is that the Content-Type is application/json, whereas the request payload is actually text/plain. That will cause a 415 Unsupported Media Type HTTP error.

You have at least two options to align then Content-Type and the actual content.

Use application/json

Keep the Content-Type as application/json and make sure the request payload is valid JSON. For instance, make your request payload this:

{
    "cookie": "=sec_session_id=[redacted]; _ga=[redacted]; AWSELB=[redacted]"
} 

Then the action signature needs to accept an object with the same shape as the JSON object.

public class CookieWrapper
{
    public string Cookie { get; set; }
}

Instead of the CookieWrapper class, or you can accept dynamic, or a Dictionary<string, string> and access it like cookie["cookie"] in the endpoint

public IActionResult GetRankings([FromBody] CookieWrapper cookie)

public IActionResult GetRankings([FromBody] dynamic cookie)

public IActionResult GetRankings([FromBody] Dictionary<string, string> cookie)

Use text/plain

The other alternative is to change your Content-Type to text/plain and to add a plain text input formatter to your project. To do that, create the following class.

public class TextPlainInputFormatter : TextInputFormatter
{
    public TextPlainInputFormatter()
    {
        SupportedMediaTypes.Add("text/plain");
        SupportedEncodings.Add(UTF8EncodingWithoutBOM);
        SupportedEncodings.Add(UTF16EncodingLittleEndian);
    }

    protected override bool CanReadType(Type type)
    {
        return type == typeof(string);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(
        InputFormatterContext context, 
        Encoding encoding)
    {
        string data = null;
        using (var streamReader = context.ReaderFactory(
            context.HttpContext.Request.Body, 
            encoding))
        {
            data = await streamReader.ReadToEndAsync();
        }

        return InputFormatterResult.Success(data);
    }
}

And configure Mvc to use it.

services.AddMvc(options =>
{
    options.InputFormatters.Add(new TextPlainInputFormatter());
});

See also

https://github.com/aspnet/Mvc/issues/5137

Upvotes: 51

Haney
Haney

Reputation: 34922

You simply need to put the body in quotes such that it represents a string. You also need to leave the request type as application/json. That way the media type formatter will figure it out:

"=sec_session_id=[redacted]; _ga=[redacted]; AWSELB=[redacted]"

Should do the trick.

Upvotes: 1

Related Questions