11
votes

I have been working on ASP.NET Core from a few weeks. I was trying to achieve something based on this blog: Microservices

My project.json is as follows:

{
  "version": "1.0.0-*",
  "compilationOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {

    "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
    "Microsoft.AspNet.Diagnostics": "1.0.0-rc1-*",
    "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
    "EntityFramework.Core": "7.0.0-rc1-final",
    "EntityFramework.Commands": "7.0.0-rc1-final",
    "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final",
    "EntityFramework.MicrosoftSqlServer.Design": "7.0.0-rc1-final",
    "Microsoft.AspNet.Mvc.Formatters.Json": "6.0.0-rc1-final",
    "Microsoft.AspNet.Mvc.Formatters.Xml": "6.0.0-rc1-final",
    "System.Security.Cryptography.Algorithms": "4.0.0-beta-23516"

  },

  "commands": {
    "web": "Microsoft.AspNet.Server.Kestrel",
    "ef": "EntityFramework.Commands"
  },

  "frameworks": {

    "dnxcore50": {
      "dependencies": {


      }

    }
  },

  "exclude": [
    "wwwroot",
    "node_modules"
  ],
  "publishExclude": [
    "**.user",
    "**.vspscc"
  ]
}

And ConfigureServices method in Startup.cs is as follows:

public void ConfigureServices(IServiceCollection services)
{
    //Registering Authorization Database
    AutorizationAccessRegisteration.RegisterComponents(services, Configuration);

    services.AddMvcCore()
        .AddJsonFormatters(a => a.ContractResolver = new CamelCasePropertyNamesContractResolver());

    //Add cors built in support.
    services.AddCors();

    services.AddMvcCore().AddApiExplorer();

    //Add MVC for supporting WebApi requests
    #region MVC Add

    services.AddMvc();

    services.AddMvc().AddMvcOptions(options =>
    {
        options.RespectBrowserAcceptHeader = true;

        // Input Formatters.
        options.InputFormatters.Clear();

        var jsonInputFormatter = new JsonInputFormatter()
        {
            SerializerSettings = new JsonSerializerSettings()
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
                ,
                DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
                NullValueHandling = NullValueHandling.Ignore
            }
        };


        options.InputFormatters.Add(jsonInputFormatter);

        //Output formater
        //as part of get/post request, set the header Accept = application/json or application/xml
        var jsonOutputFormatter = new JsonOutputFormatter();
        jsonOutputFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        jsonOutputFormatter.SerializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Ignore;
        options.OutputFormatters.Insert(0, jsonOutputFormatter);

        options.OutputFormatters.Insert(1, new XmlDataContractSerializerOutputFormatter());

    });

    #endregion
}

And here is my Confiure method in Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {

    }
    else if (env.IsStaging())
    {

    }
    else if (env.IsProduction())
    {

    }

    app.UseIISPlatformHandler();

    app.UseCors(builder =>
            builder.WithOrigins("*").AllowAnyHeader().AllowAnyMethod());

    //Middlewares addition to the pipelines:
    /// We add the middlewares in the following fashion:
    /// - Exception Handler
    /// - Logger
    /// - Authorization Handler
    /// There is a big reason of doing that.
    ///
    app.UseExceptionHandler();
    app.UseLoggerHandler();
    app.UseAuthorizationHandler();

    app.UseMvc();
}

An AuthorizationController is as follows:

[Route("api/Authorization")]
public class AuthorizationController : Controller
{
 .
     [HttpPost]
     public void Post([FromBody]string value)
     {
     }
 .
}

The Post method originally had [FromBody]string[] value. I simplified it further by making it a simple string type. I am using Advance Rest Client on Chrome to send an HTTP request. When string[] was the type I was the following values in body:

{

  ["value","sdjklgsdjlg"]

}

After simplifying the parameter, I tried sending request with the following body:

{"sdjklgsdjlg"}

tried this as well:

{"value":"sdjklgsdjlg"}

Am I missing something? I read before, the way old WebApi used to work in relation to the JSON mapping to the complex objects and normal parameters, it works in similar fashion in .NET Core.

Also I should elaborate that breakpoint is hit normally on all the middlewares and controllers. But none of the middleware seems to be able to read Request's stream related things:

context.Request variable problems

context.Request.Body errors

Kindly tell me where I am making problem. Thanks a lot!

6
What does your network sniffer say about it? Can you paste the capture of the http session? Also, can you paste the whole error message? - matcheek
What do you mean? I am using the Advance Rest Client. It shows correct Request Headers and raw body as I mentioned above. - FreshDev
have you tried it without [FromBody]? I think it will work without that - Joe Audette
I did try that before, tried just now again. But don't you think if the value was not showing up in the controller only then this [FromBody] might have been doing the problem. But that's not the case. I posted two pictures, which are of my first ExceptionMiddleware and you can see what errors are being showing up in the Body and Form variables of the request. - FreshDev
You add MVC twice, is that normal ? and MvcCore is added twice too. that looks weird - agua from mars

6 Answers

4
votes

Mark Hughes answer is corrent until some point. If you post a json of this format { "": "sdjklgsdjlg" } modelbinder should be able to bind it to simple string model, without needing the wrapper model.

4
votes

[FromBody] uses the registered formatters to decode the entire body of the submitted data into the single parameter it is applied to - by default, the only registered formatter accepts JSON.

In JSON, there is no valid way to represent a string directly - {"sdjklgsdjlg"} is not valid JSON, {"value":"sdjklgsdjlg"} is, but won't deserialize to a simple string parameter. EDIT: See the answer by @tmg, this can be done using the syntax { "": "sdjklgsdjlg" }

Therefore, you'll need some sort of specific model class to represent the input you're trying to get from the body, e.g.:

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

Then you should be able to successfully do:

[Route("api/Authorization")]
public class AuthorizationController : Controller
{
    [HttpPost]
    public void Post([FromBody]AuthRequest authReq)
    {
        // authReq.Value should have your value in
    }
}

Now if you post { "Value": "some value" } to this, it should do what you expect.

4
votes

This works for me:

[HttpPost]
public async Task<IActionResult> CreateApp([FromQuery]string userId)
{
    string appDefinition = await new StreamReader(Request.Body).ReadToEndAsync();
    var newAppJson = JObject.Parse(appDefinition);
...
2
votes

I am late here, but want to share the exact reason so any other user can get accurate info.

You are not able to get values at controller because you are posting data as JSON object:

{"value":"sdjklgsdjlg"} //See the curly braces represent an object.

For resolving this we need another object to bind this data to. Something like this at controller's action:

[HttpPost]
public void Post([FromBody]CustomViewModel data)
{
     ...
    //here you can get value as: data.Value
}

here CustomViewModel is a class:

public CustomViewModel
{
    public string Value { get; set; }
}

If you want to get data as per your current action signature:

[HttpPost]
public void Post([FromBody]string value)
{
     ...
}

Then, you need to pass data as JSON string in request body:

"sdjklgsdjlg" //notice without any curly braces and property name

Similarly for string array action:

[HttpPost]
public void Post([FromBody]IEnumerable<string> values)
{
     ...
}

pass JSON array of string in request body:

["item1", "item2"]
1
votes

You can also do something like this:

[HttpPost]
public System.Net.Http.HttpResponseMessage Post([FromBody]dynamic value)
{
   //...
}

or user [FromQuery] and pass directly a Querystring value.

0
votes

You should use the RC2 version of all your dependencies.
There is https://github.com/aspnet/KestrelHttpServer/issues/915 I have found that version of System.Threading.Tasks.Extensions is 4.0.0 by default. So you should explicitly specify the version of this package in the project.json file:

"System.Threading.Tasks.Extensions": "4.0.0-rc2-24027"