I have a class for holding both raw and encrypted values from query parameters:
public class SID
{
[FromQuery(Name = "sidInt")]
public int RawValue { get; set; }
[FromQuery(Name = "sid")]
public string EncryptedValue { get; set; }
public static implicit operator int(SID model)
{
return model.RawValue;
}
}
Having this I can successfully use it as a parameter for get requests:
/// GET: /{controller}/index?sid=my-encrypted-string
[HttpGet]
public IActionResult Index(SID id){
int resourceId = id;
//rest of the action code
}
Only encrypted value is provided in query string, raw integer value is automatically decrypted and added to the query string in my custom middleware. Notice that thanks to named [FromQuery] attributes I can use any parameter name in the action.
So far so good. But now I want to use the same SID class as a property in model for post request:
public class MyPostModel {
[Required]
[FromQuery]
public SID Id { get; set; }
public string Name { get; set; } // posted in body
// rest of the properties
}
/// POST: /{controller}/index?sid=my-encrypted-string + other fields in the body
[HttpPost]
public IActionResult Index(MyPostModel model){
int resourceId = model.Id;//model.Id is null here
//rest of the action code
}
But unfortunately I cannot make it working, Id in the model is not bound. I feel like I'm missing something obvious here. Is it possible? Do I need a custom model binder for this(I hope not, I want to use SID class as a property for several models and for different actions)?
Of course I can add sid parameter to the post action and reassign it in the action body explicitly but it looks wrong and too verbose:
[HttpPost]
public IActionResult Index(SID id, MyPostModel model){
model.Id = id;//id is correctly populated but model.Id is not
//rest of the action code
}
I think that your problem is that parameter is called "sid" and property in MyPostModel is "Id". Aside from that, I don't see any reason why it would not work. Can you change your MyPostModel:
public class MyPostModel {
[Required]
[FromQuery(Name = "sid")]
public SID Id { get; set; }
public string Name { get; set; } // posted in body
// rest of the properties
}
A model class userQuery that I have written isn't showing up in Swagger UI. I have referenced it in my controller file so I expected it to show up in the Swagger UI. I use SwashBuckle. What am I missing here?
The controller file having an endpoint:
using Project.Models;
namespace Project.Controllers
{
[HttpGet]
[Authorize(Policy = "Read-Run")]
[Route("byRoute/{element}")]
[Produces(typeof(EntityResult<EntityResponse>))]
public async Task<IActionResult> ListEntities([FromQuery] userQuery entityMatch, string element)
{
return Ok((await _entityService.ListEntities(entityMatch, element)));
}
}
Model class:
using System;
namespace Project.Models
{
public class UserQuery
{
public int Id { get; set; }
public DateTime? DateCreated { get; set; }
public DateTime? DateUpdated { get; set; }
}
}
It seems you can't bind complex objects to query string parameters in Swashbuckle (or OpenAPI): check this question.
You can use three different parameters: Id, DateCreated, and DateUpdated in your controller and use the [FromQuery] attribute for each of them.
Your endpoint will look like this:
public async Task<IActionResult> ListEntities(
[FromRoute] string element,
[FromQuery] int id,
[FromQuery] DateTime? dateCreated,
[FromQuery] DateTime? DateUpdated
)
Or you can pass the object in the body of the request and use the [FromBody] attribute. But usually, you shouldn't use the body for a GET request.
I try to make GET request via WebApi with complex object.
Request is like this:
[HttpGet("{param1}/{param2}")]
public async Task<IActionResult> GetRequest(string param1, int param2, [FromBody] CustomObject[] obj)
{
throw new NotImplementException();
}
Where CustomObject is:
[DataContract]
public class CustomeObject
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
}
How do I compose a valid GET request?
[FromBody] CustomObject[] obj ... GET request has no message body and thus you should change it to FromUri.
Sure, take a look at Documentation
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public ValuesController : ApiController
{
public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}
Request would be like below, essentially you pass the entire object data as query string
http://localhost/api/values/?Latitude=47.678558&Longitude=-122.130989
An array of object example can be found in another post pass array of an object to webapi
If your complex object is defined by the server, you can model bind to it through the URI and dot notate the properties in the routing template. My advice is to keep this model to one level of properties. You can bind to more complex objects, but you'll quickly find yourself having to write your own model binder.
Note that your argument decorator will need to be changed to [FromUri] to bind a complex object through the Uri. Servers are not required to support GET bodies and most do not.
public class CustomObject
{
public string Name { get; set; }
public string Email { get; set; }
}
[HttpGet]
[Route("{foo.Name}/{foo.Email}")]
public HttpResponseMessage Get([FromUri]CustomObject foo)
{
//...body
return Request.CreateResponse(HttpStatus.OK, foo);
}
You can pass it as a stringified json or use the request body via post instead of get.
I catch post request from 3rd-side static page (generated by Adobe Muse) and handle it with MVC action.
<form method="post" enctype="multipart/form-data">
<input type="text" name="Name">
...
</form>
Routing for empty form action:
app.UseMvc(routes => routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}"));
But in according action I have model with every property is empty
Action:
[HttpPost]
public void Index(EmailModel email)
{
Debug.WriteLine("Sending email");
}
Model:
public class EmailModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
public string Phone { get; set; }
public string Additional { get; set; }
}
Request.Form has all values from form, but model is empty
[0] {[Name, Example]}
[1] {[Email, Example#example.com]}
[2] {[Company, Hello]}
[3] {[Phone, Hello]}
[4] {[Additional, Hello]}
Be careful not to give an action parameter a name that is the same as a model property or the binder will attempt to bind to the parameter and fail.
public async Task<IActionResult> Index( EmailModel email ){ ... }
public class EmailModel{ public string Email { get; set; } }
Change the actions parameter 'email' to a different name and it will bind as expected.
public async Task<IActionResult> Index( EmailModel uniqueName ){ ... }
I'm not sure it is same case, but I had same problem and nothing really looks to work for me.
The problem in my case was that I had a property called Model in my view model class
public string Model { get; set; }
When I renamed the property to ModelName everything was working fine again, even without FromForm attribute.
Looks like some special property names could be a bit of a problem for asp.net mvc model binding.
So, my advice is to check your model properties and maybe try renaming them one by one in order to check if the problem is there.
Hope this helps.
ASP.Net Core 3.1
In my case, I was using a complex model (a model that contains other models, like a shared model) posted by Ajax, so the inputs in the view were automatically named like this "ChildModel.PropertyName" (see code)
[HttpPost]
[ValidateAntiForgeryToken] // ("AUVM.PropertyName")
public async Task<JsonResult> AddUser([FromForm]AUVM aUVM) //Parameter name must match the first part of the input name in order to bind
{
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<JsonResult> AddUser([FromForm]AUVM someUniqueName) //This is wrong and won't bind
{
}
I'm having the same problem
this docs helps me to understand Model Binding
https://docs.asp.net/en/latest/mvc/models/model-binding.html
I solved my problem by making sure that the property name is exact match in form field name
and I also add [FromForm] attribute to specify exactly the binding source.
I ran into this today, and though in hindsight it seems obvious, just thought I'd add that you need to make sure your access modifiers for the Properties on the model you're binding are correct. I had public MyProperty { get; internal set; } on some and they would not bind. Removed internal and they worked just fine.
Change void to ActionResult.
[HttpPost]
public ActionResult Index(EmailModel email)
And don't forget verifying AntiForgeryToken from your view and action.
// to your form in view
#Html.AntiForgeryToken()
// ------------
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(EmailModel email)
This issue can also happen if one or more of your properties in the request model fail to bind into an acceptable on the request model.
In my case, I was supposed to pass a List<string> type to a property, but accidentally passed a string. This resulted in the entire request model becoming null
In my case, I was using the MVC 4.0 convention of naming the object you are posting. E.g.,
js: $http.post("SaveAnswer", { answer: answerValues })
C#: public ActionResult SaveAnswer([FromBody] AnswerVM answer) {...}
When I changed the js to $http.post("SaveAnswer", answerValues), everything works.
I have run in to this issue where the model does not bind when I have more than one constructor on the underlying model like:
public class EmailModel
{
public EmailModel()
{}
public EmailModel(string _name, string _company)
{
Name = _name;
Company = _company;
}
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
public string Phone { get; set; }
public string Additional { get; set; }
}
Fixed by removing all but one constructor like this:
public class EmailModel
{
public EmailModel()
{}
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
public string Phone { get; set; }
public string Additional { get; set; }
}
I had the same problem and I want to share what happened in my case and how I got the solution. Perhaps, someone may find it useful to know.
My context was ASP.NET Core 3.1 Razor pages.
I had a handler as
public async Task<JsonResult> OnPostLogin([FromForm] LoginInput loginData)
{
}
and also I had a property in my ViewModel as
public LoginInput LoginInput { get; set; }
And in my Razor page, I have used the form like this:
<form asp-page-handler="Login" method="post">
<input asp-for="LoginData.UserNameOrEmail">
.....
So, notice that, in my form, I used the property LoginInput.UserNameOrEmail, but in my handler's parameter, I used the parameter name loginData. How will the ASP.NET Core know that, this LoginInput.UserNameOrEmail is actually loginData.UserNameOrEmail. It can't, right? Therefore, it did not bind my form data.
Then, when I renamed my ViewModel property "LoginInput" to the same name as the handler parameter, like this:
public LoginInput LoginData { get; set; }
The ASP.NET Core then found that the form data was matching the parameter name and then it started to bind properly. My problem was solved.
I have a model called PostUserAccount, and I'm trying to use it in an ApiController as a parameter the way it normally is when you generate a controller with read/write actions, using Entity Framework
Example generated by controller generator:
// POST api/Profile
public HttpResponseMessage PostUserProfile(UserProfile userprofile)
{
if (ModelState.IsValid)
{
db.UserProfiles.Add(userprofile);
...etc
code that I'm working with:
// POST gamer/User?email&password&role
public HttpResponseMessage PostUserAccount(PostAccountModel postaccountmodel)
{
if (!ModelState.IsValid)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, ModelState);
}
if (postaccountmodel == null) return Request.CreateResponse(HttpStatusCode.BadRequest, "model is null");
...etc
for whatever reason, the postaccountmodel is null in this case, and running this api command returns "model is null". any ideas?
Here is the model in question
public class PostAccountModel
{
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
public string Role { get; set; }
public string Avatar { get; set; }
public string DisplayName { get; set; }
}
You're trying to send the model in the URI query string. Problems:
Your query string is not well formed - should be ?Email=xxx&Password=xxx& ...
You need to decorate the postaccountmodel parameter with the [FromUri] attribute, to tell Web API to bind the model from the URI
Another option is to send the model in the request body as JSON or XML. I'd recommend that, especially if you're really sending a password in the request. (And use SSL!)
This topic describes parameter binding in Web API: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
Try putting the [FromBody] attribute in front of your postaccountmodel parameter.
http://msdn.microsoft.com/en-us/library/system.web.http.frombodyattribute(v=vs.108).aspx