I have a simple question. I am building an HTTP REST service in ASP.NET WebApi 4 and I am having some trouble getting my model bindings to work.
I am using the following code to accept a POST HTTP request and process a login. From what I can gather ASP.NET WebApi 4 will deserialize the JSON for you and bind to the accepted model. I have setup my model, but whenever I test the service via the debugger I get an NullReferenceExecption on the UserPostData object.
From what I can tell I have everything setup correct, but it's just not working. Below is my JSON that I am posting. Does anyone have any idea why I am getting this error?
JSON
[
{
"Username": "mneill",
"Password": "12345"
}
]
Code From WebApi 4 Controller Class
public class UserPostData
{
public string Username { get; set; }
public string Password { get; set; }
}
public class UserController : ApiController
{
//
// GET: /User/
public string[] Get(string username)
{
return new string[]
{
"username",
username
};
}
public HttpResponseMessage Post([FromBody] UserPostData body)
{
//string username = postData.Username;
//string password = postData.Password;
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
if (body.Username == null)
response.StatusCode = HttpStatusCode.NotFound;
if (body.Password == null)
response.StatusCode = HttpStatusCode.NotFound;
return response;
}
}
Make sure that the Content-Type header is present in your request.
Modify your Json to be like below:
{ "Username": "mneill", "Password": "12345" }
And also add the following code in your Post action to see any model binding errors:
if (!ModelState.IsValid)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState));
}
I don't know if this is only your formatting, but your current JSON represent array that contains one element of type UserPostData. If that's true change your request to send object instead of array or change your controller to support arrays.
BTW I thing that FromBody is default behavior for complex types like your class.
Related
Input JSON:
{ "name": "gerry" }
Action method:
{ public ActionResult GenerateQrCode([FromBody] string name }
Problem:
The simple-type args are null
ModelState: Invalid
The built-in json deserializer can't handle the input in this form
I've tried:
ConfigureServices() -> services.AddControllersWithViews().AddNewtonsoftJson(); to switch to NewtonSoft, which I know/love
I've set a break-point into the non-NewtonSoft built-in MS SystemTextJsonInputFormatter.ctor() just to check, if it's still used: yes, it is, I'm not sure why, when I'm calling the above .AddNewtonsoftJson()
The situation:
The client POSTs all the input params as one JSON string document, which is UTF8 w/out BOM
The string comes in at the server-side and is nicely readable with new System.IO.StreamReader(Request.Body).ReadToEnd() from inside the immediate window
I need a way ASP.NET Core deserializes this, as it was able under the .NET4.X for many years w/out any issue
I wouldn't like to add [FromBody] and similar opt-in signatures all over the server actions/args
You pass the name as json but accept as a string so it will be null, you can use an InputFormatter like:
public class RawJsonBodyInputFormatter : InputFormatter
{
public RawJsonBodyInputFormatter()
{
this.SupportedMediaTypes.Add("application/json");
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var request = context.HttpContext.Request;
using (var reader = new StreamReader(request.Body))
{
var content = await reader.ReadToEndAsync();
return await InputFormatterResult.SuccessAsync(content);
}
}
protected override bool CanReadType(Type type)
{
return type == typeof(string);
}
}
In startup.cs:
services
.AddMvc(options =>
{
options.InputFormatters.Insert(0, new RawJsonBodyInputFormatter());
});
And then you can get the row string
To deserilize it, you can check this, use Newtonsoft and make the string to a Model
[HttpPost]
public IActionResult GenerateQrCode([FromBody] string name)
{
object o = JsonConvert.DeserializeObject(name);
MyModel my = JsonConvert.DeserializeObject<MyModel>(o.ToString());
return View();
}
Here users pass to the method normally:
#PutMapping
#RequestMapping("/update_user")
public String update(#RequestBody List<CustomUser>users) {
return ......
}
Postman PUT request body:
[
{"name":"Name1"},
{"name":"Name2"}
]
But here I receive an error: "Failed to resolve argument 1 of type CustomUser":
#PutMapping
#RequestMapping("/update_user")
public String update(#RequestBody CustomUser user1, CustomUser user2) {
return ......
}
Postman PUT request body:
{
"user1":{"name":"Name1"},
"user2":{"name":"Name2"}
}
What am I doing wrong?
RequestBody annotated parameter is expected to hold the entire body of the request and bind to one object, so you essentially will have to go with one object only
Now, Either you can wrap them both like this
public class Payload {
CustomUser user1;
CustomUser user2;
//getters & setters
}
and use that for RequestBody
#PostMapping
#RequestMapping("/update_user")
public String update(#RequestBody Payload users) {
return ......
}
Or you can use a Map<String, CustomUser> for RequestBody
#PostMapping
#RequestMapping("/update_user")
public String update(#RequestBody Map<String, CustomUser> users) {
//you can access like this
CustomUser user1 = users.get("user1");
CustomUser user2 = users.get("user2");
}
Another thing to note, you are mapping as POST request but your comment says "PUT". check that as well.
I have created one method in mvc api which returns string. But instead of returning string, I want to return Json Object. Here is my code.
[AllowAnonymous]
[HttpPost]
[Route("resetpassword")]
public IHttpActionResult ResetPassword(string email)
{
CreateUserAppService();
string newPassword =_userAppService.ResetPassword(email);
string subject = "Reset password";
string body = #"We have processed your request for password reset.<br/><br/>";
string from = ConfigurationManager.AppSettings[Common.Constants.FromEmailDisplayNameKey];
body = string.Format(body, newPassword, from);
SendEmail(email, subject, body, string.Empty);
return Ok<string>(newPassword);
}
Here it returns Ok<string>(newPassword); Now I want to return Json object. How can I return Json object?
Try that:
[AllowAnonymous]
[HttpPost]
[Route("resetpassword")]
public IHttpActionResult ResetPassword(string email)
{
//...
return Json(newPassword);
}
You are actually already using the key thing...
[HttpGet]
public IHttpActionResult Test()
{
return Ok(new {Password = "1234"});
}
You need to return it as CLR object so Web API serialize it to JSON, you can create your own POCO class or do it like this:
var passResponse = new
{
newPassword= yourNewPassword
};
But from security standpoint what you are doing is not correct, you should NEVER send plain passwords by email, you should reset user password by providing them a reset email link to your portal with some token and they should enter the new password. What you are doing here is not secure.
Create a return object.
public class PasswordResponse{
public string Password {get;set;}
//...other stuff to pass...
}
Then return an instance of the type in your response.
return OK(new PasswordResponse(){Password = newPassword});
I'm new at MVC and can't get this to work. I basically have a Users class, a UserRepository, and a IUser interface.
This is my code:
public class Users
{
public string UserName { get; set; }
public string Department { get; set; }
public string UserType { get; set; }
}
public class UsersRepository : TimeAttendanceMVC.Models.IUsers
{
public Users Return_UserName_Dept()
{
Users U = new Users();
List<Users> LoggedInUser = new List<Users>();
U.UserName = "TestUser";
U.Department = "Finance";
U.UserType = "Administrator";
LoggedInUser.Add(U);
//string json = JsonConvert.SerializeObject(LoggedInUser, Formatting.Indented);
//return json;
return Json(LoggedInUser.ToArray(), JsonRequestBehavior.AllowGet);
}
}
namespace TimeAttendanceMVC.Models
{
public class IUsers
{
List<Users> Return_UserName_Dept();
}
}
There are a few errors that I get. In UsersRepository.cs where i'm returning Json, the error says that "The name Json does not exist in the current context". The error from IUsers.cs is that "Return_UserName_Dept() must declare a body because it is not marked abstract...".
Can anybody please help me with this. I just don't know how this is supposed to work and i'm trying to learn MVC by working on this application. It's actually the FullCalendar application found here - link to FullCalendar. I'm trying to turn it into an MVC application.
EDIT:
Maybe I need to do this:
public JsonResult Return_UserName_Dept()
instead of public Users Return_UserName_Dept()
You should be doing this on your controller in some method which returns a json action (jsonresult). The repository should be returning your data only and all the operations you need to do, whether you're converting data to json or any other logic should happen at the controller or at some helper class which would be called by the controller..
Edit:
In order to have a method which returns a JsonResult, you need to have a reference to System.Web.Mvc.ActionResult and since the repository is usually at the model, you won't have this reference.. another thing is that you might be breaking your design the logic should be available at the controller for what you want
Edit 2:
The code below is from an old post you can see here. Note how the action PopulateDetails gets the user object from the repository and that's all the repository does.. the actual logic is happening inside this method, such as populate the rest of the UserModel class, and then it returns the JsonResult:
public JsonResult PopulateDetails(UserModel model)
{
UserResultModel userResultModel = new UserResultModel();
if (String.IsNullOrEmpty(model.UserId))
{
userResultModel.Message = "UserId can not be blank";
return Json(userResultModel);
}
User user = _userRepository.GetUser(model.UserId);
if (user == null)
{
userResultModel.Message = String.Format("No UserId found for {0}", model.UserId);
return Json(userResultModel);
}
userResultModel.LastName = user.LastName;
userResultModel.FirstName = user.FirstName;
userResultModel.Message = String.Empty; //success message is empty in this case
return Json(userResultModel);
}
I am looking for sending JSON object to the server via GET.
Chris's answer on Post an Array of Objects via JSON to ASP.Net MVC3 works for the http POST but not for GET.
My case also works for POST but not for GET. What can I do to make GET work
Here is my case:
in Controller I have the following method
public ActionResult Screenreport(Screentable screendata)
{
// do something here
return View();
}
I have two ModelView as follows:
public class Screenrecord
{
public string Firstname{ get; set; }
public string Lastname{ get; set; }
}
public class Screentable
{
public List<Screenrecord> Screenlist { get; set; }
}
On the client side I generate JSON object
var Screentable = { Screenlist: screendata };
screendata is an array of Screenrecord
All this work when I use POST but when I use GET I am getting null value (screendata = null) Controllers' method.
In other word when click GO, screendata is null in Screenreport(Screentable screendata) routine.
Also, if I send one JSON object it works but if I send an array (list) like I described, it does not.
Is what I am trying to do doable?
No :-)
Thats not how get works.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
(see 9.3 GET)
"The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI"
Request-URI being the important part here. There is no concept of body data in a GET request.
Try changing method to public ActionResult Screenreport(HttpRequestMessage request)
Then use below code to get JSON object.
data = request.RequestUri.Query;
data = HttpUtility.ParseQueryString(data).Get("request");
Try this example in Javascript:
var someObject = {
id:123456,
message:"my message",
}
var objStr = JSON.stringify(someObject);
var escapedObjStr = encodeURIComponent(objStr);
var getUrlStr = "http://myserver:port?json="+escapedObjStr
and now you can forward this URL to your server. I know this is not in any .NET language but you can definitely find the equivalent methods used, or just use the JS right away.