I had
[HttpPost]
public ActionResult Foo()
{
// read HTTP payload
var reqMemStream = new MemoryStream(HttpContext.Request.BinaryRead(HttpContext.Request.ContentLength));
....
}
The payload is application/json; worked fine; then I changed to
public ActionResult Foo(string thing)
{
....
}
The intention being to post to MyController/Foo?thing=yo
Now I cant read the payload(the length is correct but the stream is empty). My guess is that the controller plumbing has eaten the payload looking for form post data that can be mapped to the method parameters. Is there some way that I can stop this behavior (surely MVC should not have eaten a payload whose type is marked as JSON , it should only look at form post data). My work around is to add 'thing' to the json but I dont really like that
Try resetting the input stream position before reading:
public ActionResult Foo(string thing)
{
Request.InputStream.Position = 0;
var reqMemStream = new MemoryStream(HttpContext.Request.BinaryRead(HttpContext.Request.ContentLength));
....
}
Now this being said, if you are sending an application/json payload why on the holy Earth are you bothering to read directly the request stream instead of simply defining and using a view model:
public class MyViewModel
{
public string Thing { get; set; }
public string Foo { get; set; }
public string Bar { get; set; }
...
}
and then:
public ActionResult Foo(MyViewModel model)
{
// use the model here
....
}
ASP.NET MVC 3 has a built-in JsonValueProviderFactory which allows you to automatically bind JSON requests to models. And if you are using an older version it is trivially easy to add such factory your self as Phil Haack illustrates in his blog post.
Related
The query string I use is always empty. I have no idea why, and have tried for hours with
The HttpContext.Request returns all other parts of the URL except the querystring.
With this url https://localhost:44394/Trackers/Create?Place=Vision_College
and this Model
[BindProperties(SupportsGet = true)]
public partial class Tracker
{
[FromQuery(Name = "Place")] //populates it from the query
public string Place { get; set; }
...}
and this controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Name, Phone, Place")] Tracker tracker)
{
OK I found an answer.
I was trying to use it in the POST of the CREATE, when I should have been using it in the GET part of CREATE
Thanks for everyones help!
Since you are using the query parameters in httpPost you should use, [FromQuery] inside your arguments. Follow this
Your DTO class would be,
public class Tracker
{
[FromQuery(Name = "Place")]
public string Place{ get; set; }
}
In your controller class
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([FromQuery]Tracker tracker)
{
}
Note: If your query parameters match with the model property names, specifically annotating the properties would not be necessary.
Better you can get by body itself since this is a post request. otherwise make this as a get request. If converting to get by body, simply use [FromBody] in endpoint arguments instead of [FromQquery]
So I'm trying to add a "like" function to some blog posts by POSTing some JSON through ajax to the MVC api controller.
Models:
public class Blog{
public int ID{get;set;}
public string Title{get;set;}
//Some more stuff
public virtual ICollection<Likes> Likes {get;set;}
}
public class Likes{
public int ID{get;set;}
public string UserName{get;set;}
//Some more stuff
public virtual Blog Blog{get;set;}
}
Controller:
public void Post(Likes like){
if(ModelState.IsValid){
db.Likes.Add(like)
db.SaveChanges();
}
}
The below return is passed into the JSON.stringify() in the ajax call:
var UserName = "Dave";
//More stuff
var Blog = "????";
return {UserName:UserName, More Stuff, Blog:Blog}
So that last line is my problem I think, what do I pass in for "Blog"? I have tried using the ID of the Blog but it's still null in the controller. It seems the controller wants a full Blog model instead of just the ID, is there a way around this or am I doing something bad?
TLDR I guess is: What to pass to a field that's a public virtual Blog Blog{get;set;} in the model when sending JSON to the controller?
P.S. My code works when I don't include the "Blog" as part of the JSON.
if you are posting a like then this would probably do the thing
{
ID : 789 //ID of the Like
UserName : "Username",
Blog : {
ID : 789 //id of the blog
}
}
This way you will have the id of the blog and would be mapped correctly.
But i would advise you make a separate view model of the following structure
public LikeAjax
{
public string UserName{get; set;}
public int BlogId {get; set;}
}
then send json like below
{
UserName : "User Name",
BlogId : 789
}
I have just started learning WebAPI today and I can't figure out why "account" is always null.
Request
Content-Type: application/json; charset=utf-8
Request-Body: {"account":{"email":"awd","password":"awad","isNewsletterSubscribed":false}}
WebAPI
public class AccountsController : ApiController
{
public void Post([FromBody] string account)
{
// account is null
}
}
Shouldn't account contain a json string in this case?
Shouldn't account contain a json string in this case?
That would depend on the specific Content-Type request header you set when you send the request. For example if you used application/x-www-form-urlencoded which is the default then your request body payload must have looked like this:
={"account":{"email":"awd","password":"awad","isNewsletterSubscribed":false}}
Notice the = character at the beginning. That's one of the biggest weirdest things I have ever encountered. Since you can bind only one parameter from the body if the request the Web API doesn't expect a parameter name, but just the value.
This being said, your request payload looks more like a JSON. So it would make far more sense to design a view model and use Content-Type: application/json when sending the request. Binding a JSON object to a string is not common practice.
So:
public class UserViewModel
{
public string Email { get; set; }
public string Password { get; set; }
public bool IsNewsletterSubscribed { get; set; }
}
public class AccountViewModel
{
public UserViewModel Account { get; set; }
}
and then your controller action will simply take the view model as parameter. In this case yo udon't need to decorate it with the [FromBody] attribute because by convention in Web API the default model binder will attempt to bind complex types from the body of the request:
public class AccountsController : ApiController
{
public HttpResponseMessage Post(AccountViewModel model)
{
// work with the model here and return some response
return Request.CreateResponse(HttpStatusCode.OK);
}
}
Also notice that since HTTP is a request/response protocol it makes much more sense to have your Web API controller actions return response messages as shown in my example rather than just having some void methods. This makes the code more readable. You immediately understand how the server will respond and with what status code to the specified request.
I am in the process of migrating PHP code to ASP.NET MVC and previously for the register page I would store if the new user had accepted the rules and also was COPPA verified by redirecting from /register to /register&readrules=1&coppa=1. I would then just parse the #readrules and #coppa in the code.
What is the best way to do this in ASP.NET? Thanks
Use query string parameters instead:
/register?readrules=1&coppa=1
This is more standard and you do not need any parsing. Just define a view model to accomodate those values:
public class MyViewModel
{
public int Readrules { get; set; }
public int Coppa { get; set; }
}
and ten have your Register controller action take this view model as parameter:
public ActionResult Register(MyViewModel model)
{
... at this stage model.Readrules and model.Coppa will contain the values passed
as query string parameters tat you could use here
}
The default model binder will automatically bind the values of the readrules and coppa query string parameters to the corresponding properties of the view model that your controller action takes.
I'm new to asp.net mvc. Basically i'm from php programmer. In my php file i can display what are all the values coming from html page or form using echo $_POST; or print_r($_POST); or var_dump($_POST). But in asp.net how can i achieve this to check what are all the values are coming from UI Page to controller.
You may take a look at the Request.Form property:
public ActionResult SomeAction()
{
var values = Request.Form;
...
}
You could put a breakpoint and analyze the values. Or simply use a javascript development toolbar in your browser (such as FireBug or Chrome Developer Toolbar) to see exactly what gets sent to the server.
But normally you are not supposed to directly access the raw values. In ASP.NET MVC there's a model binder which could instantiate some model based on the values sent to the server.
For example you could have the following model:
public class MyViewModel
{
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
and then have your controller action take this model as parameter:
public ActionResult SomeAction(MyViewModel model)
{
... you could use the model properties here
}
and now you could invoke this controller action either wityh a GET request passing the parameters in the query string (/someaction?age=10&firstname=foo&lastname=bar) or using a POST and sending them in the body.
You can check the raw data via Request.Form.
But this is not he spirit of the ASP.NET MVC. It is preferd that you expect a model into your controller. You have all type safety mapping already done by special module called model binder.
So unless you work on some special case, you just add a model to the controller action:
public ActionResult SomeAction(SomeModel model)
{
//Handle SomeModel data further ...
}
You can create an action which will accept the parameters from the UI page like the following:
[HttpPost]
public ActionResult SomeAction(string param1, int param2)
{
//Now you can access the values here
}
or make an action which will accept the model
public ActionResult SomeAction(SomeModel model)
{
//Access the model here
}