I am using Newtonsoft.Json to deserialize a JSON object to a model. One of the strings in my object is:
mQvhIBYwGVtLQYtoSgUsHBxcvRcRiOpb94zqwYPF8Cz1scPia1pR4BgGZ2ThPv+NhXGlLFK/ZbHy/b3YzjKwBw==
I am finding that after RequestFolioDetailModel requestFolioDetailModel = JsonConvert.DeserializeObject(criteria); within the controller the value of User.SessionId is removing the '+' and saving the value with a space instead of the '+'.
Just to add this is a MVC Application. I do a Ajax call to the controller passing through a criteria and then serialise it into an object. See code snippets below:
Code
Javascript function:
function GetNotesForCustomer() {
var data = {
FolioId: gModel.SearchPageModel.Body.VfolFolioId,
User: {
SessionId: 'mQvhIBYwGVtLQYtoSgUsHBxcvRcRiOpb94zqwYPF8Cz1scPia1pR4BgGZ2ThPv+NhXGlLFK/ZbHy/b3YzjKwBw=='
},
Note: {
Voided: '0'
}
};
$.ajax({
url: gBaseUrl + 'Home/SearchCustomerNotes',
type: 'post',
cache: false,
async: true,
data: "criteria=" + JSON.stringify(data),
beforeSend: function () {
//Do Something
}, complete: function () {
//Do Something
},
error: function (result) {
//Do Something
},
success: function (result) {
//Do Something
}
});
}
Controller:
[Route("Home/SearchCustomerNotes/{criteria?}")]
public ActionResult SearchCustomerNotes(string criteria)
{
ActionResult actionResult;
RequestFolioDetailModel requestFolioDetailModel = JsonConvert.DeserializeObject<RequestFolioDetailModel>(criteria);
// do something with request Folio Detail Model
return actionResult;
}
Models:
public class RequestFolioDetailModel
{
public string FolioId {get; set;}
public RequestUserModel User {get; set;}
public RequestNoteModel Note {get; set;}
public RequestFolioDetailModel()
{
User = new RequestUserModel();
Note = new RequestNoteModel();
}
}
}
public class RequestUserModel
{
public string Login {get; set;}
public string Password {get; set;}
public string SessionId {get; set;}
public RequestUserModel()
{
Login = string.Empty;
Password = string.Empty;
SessionId = string.Empty;
}
}
How do I stop it from doing this as I need the + to stay in?
The problem is that you are passing your JSON from client to server as application/x-www-form-urlencoded content (the default content type for jQuery.ajax), but you are not URL encoding it.
The application/x-www-form-urlencoded content type requires the body of the request to contain URL encoded key-value pairs separated by & characters, just like in a URL query string. When the MVC server sees this content type, it applies a URL decode operation on each pair in the body, which will cause + characters to turn into spaces if the data is not properly encoded.
The quick fix, of course, is to encode your JSON string on the client side just after stringifying it:
$.ajax({
url: gBaseUrl + 'Home/SearchCustomerNotes',
type: 'post',
cache: false,
async: true,
data: "criteria=" + encodeURIComponent(JSON.stringify(data)),
...
});
However, since your data is actually JSON, a better solution is to actually send it as application/json content and take advantage of the built-in deserialization that the MVC framework provides. To do this, you need to make a couple of adjustments to your code on both client and server. On the client side, add a contentType specifier to your ajax call specifying the data is application/json. Then, remove the criteria= part from the data string. You do not need to do any URL encoding with this content type. Here is what the client side code would look like:
$.ajax({
url: gBaseUrl + 'Home/SearchCustomerNotes',
type: 'post',
cache: false,
async: true,
contentType: "application/json",
data: JSON.stringify(data),
...
});
On the server side, change your method to accept a RequestFolioDetailModel object directly. You can remove the DeserializeObject call from the body of the method, since the MVC framework will take care of deserializing the object for you. And of course, you need to adjust your Route attribute to remove the criteria part. You may also want to add HttpPost to it while you're at it.
[HttpPost, Route("Home/SearchCustomerNotes")]
public ActionResult SearchCustomerNotes(RequestFolioDetailModel requestFolioDetailModel)
{
// do something with request Folio Detail Model
...
}
Related
I tried to pass more than one parameter from client-side using jQuery to ASP Web API method, but the method can't accept that. I tried some of the solutions but the same thing.
Web API:
[HttpPost]
[ResponseType(typeof(Message))]
[Route("api/Messages/send-message")]
public async Task<IHttpActionResult> SendMessage(Email email, Message message){}
jQuery:
$.ajax({
url: '/api/Messages/send-message',
method: 'POST',
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify({
email: EmailsArray,
title: $('#txtTitle').val(),
body: $('#txtContent').val(),
}),
success: function (response) {
console.log(response);
});
Error Message:
"message":"An error has occurred.","exceptionMessage":"Can't bind multiple parameters ('email' and 'message') to the request's content.","exceptionType":"System.InvalidOperationException"
If you're sending the data as JSON then all the data needs to be contained in a single coherent JSON structure. Having two separate input parameters on the server side does not fit with this concept.
In this situation you can create a DTO (Data Transfer Object) which is a class holding all of the items you want to transfer. Something like this:
public class EmailMessageDTO
{
public Email email { get; set; }
public Message message { get; set; }
}
Then you define the action method as accepting this single over-arching object
public async Task<IHttpActionResult> SendMessage(EmailMessageDTO dto) { }
And in the JavaScript:
data: JSON.stringify({
email: EmailsArray,
message: {
"title": $('#txtTitle').val(),
"body": $('#txtContent').val(),
}
}),
It's quite similar to the concept of having a ViewModel in MVC.
You need to add [FromBody] attribute to parameter and you need to make post data to application/x-www-form-urlencoded and you send only email parameter, you need to add message parameter if you wanna send title and body you need to create model like
model:
public class EmailSendModel(){
public string email{get;set;}
public string title{get;set;}
public string body{get;set;}
}
controller:
[HttpPost]
[ResponseType(typeof(Message))]
[Route("api/Messages/send-message")]
public async Task<IHttpActionResult> SendMessage([FromBody]EmailSendModel model){}
I am creating a client-side script to send a dictionary object type to the web api method using $http as follows:
$scope.SearchPolicyDetails = function (data) {
var searchPolicy = new Array();
searchPolicy["InsuredName"] = data.InsuredName;
searchPolicy["PostalCode"] = data.PostalCode;
searchPolicy["LOB"] = data.LOB;
searchPolicy["AgencyName"] = data.AgencyName;
searchPolicy["Symbol"] = data.Symbol;
searchPolicy["PolicyNum"] = data.PolicyNum;
searchPolicy["MCO"] = data.MCO;
searchPolicy["expireswithin"] = data.expireswithin;
searchPolicy["SortFields"] = data.SortFields;
searchPolicy["SortOrder"] = data.SortOrder;
$http({
url: "http://localhost:53054/api/GetPoliciesBySearch",
dataType: 'json',
data: searchPolicy,
headers: {
"Content-Type": "application/json"
}
}).success(function (response) {
$scope.value = response;
})
};
and I have this WebAPI method:
public List<Dictionary<string,string>> GetPoliciesBySearch(Dictionary<string,string> policySearch)
{
return SpecializedHandler.GetAllPolicies(policySearch).IterativeResource;
}
But I am not receiving the object to the method.
I am seeing this error in the chrome console :
I think your code and UI are in different projects, might be you have not configured CORS in web.config or WebApiConfig.cs. You can follow this URL
https://msdn.microsoft.com/en-us/magazine/dn532203.aspx
Instead of using a dictionary in your API action you need to define a class and use that.
class PolicyRequest
{
public string InsuredName { get; set; }
public string PostalCode { get; set; }
public string LOB { get; set; }
...
}
searchPolicy["InsuredName"] = searchPolicy.InsuredName. This is not an array but a json object with attributes like InsuredName and so. to make it into an array. You can do :
var searchPolicy = [];
searchPolicy.push(data.InsuredName);
Looks like there are multiple things to consider here:
First: The object being created clientside isn't going to translate to a List<Dictionary<string, string>> so for this to work as we would like we will have to make some alterations. Consider:
var searchPolicy = {};
searchPolicy['InsuredName'] = data.InsuredName;
searchPolicy['PostalCode'] = data.PostalCode;
//etc (see below for another crucial piece)
Second: The code calling inside of the $http isn't targeting any particular method (see the docs). Consider something along the lines of:
$http({
url: "http://localhost:53054/api/GetPoliciesBySearch",
dataType: 'json',
method: 'POST',
data: JSON.stringify(searchPolicy),
headers: {
"Content-Type": "application/json"
}
}).then(successHandler, errorHandler);
//try to avoid the deprecated success / error functions
function successHandler(response){
$scope.value = response;
}
function errorHandler(response){
//I strongly consider this for debugging
console.log(response);
}
Third: Consider accepting a standard Dictionary<string, string> in the WebAPI controller since the new object shouldn't be a list of dictionaries but rather a flat dictionary. (this answer may provide more context)
Finally: It looks like routing might be confused judging from the error messages; ensure that there is a route setup in the WebApiConfig.cs similar to:
RouteTable.Routes.MapHttpRoute("GetPoliciesBySearch",
"api/getpoliciesbysearch",
defaults: new
{
controller = "{your controller name here}",
action = "GetPoliciesBySearch"
});
Currently I can to create models regularly (by going SettingProfile/Create)
But when I try to use AJAX to send data wrapped in settingProfile JS object it returns HTTP 500 Internal Server Error error, I believe problem is in the data type. What would be correct way of calling Create method in AJAX?
My code:
Model:
public class SettingProfile
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public long? UserId { get; set; }
public string Url { get; set; }
}
View (JS):
function saveSettingProfile() {
var name = prompt("Please enter profile name", "");
var url = $("form").serialize(); // Not AJAX url, its a variable in model
var settingProfile = {
Name: name,
Url: url
};
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
contentType: "application/json; charset=utf-8",
dataType: "json",
method: "POST",
data: settingProfile
}).done(function (response) {
alert("Profile saved successfully");
}).fail(function () {
alert("Could not save profile");
});
}
Controller:
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Name,Url")] SettingProfile settingProfile)
{
settingProfile.UserId = 8; // TODO: get user id from session
if (ModelState.IsValid)
{
db.SettingProfiles.Add(settingProfile);
db.SaveChanges();
return Json(new { success = true });
}
return Json(new { success = false });
}
500 error means, your server code is crashing. It could be of many reasons. One thing i noticed (which might cause a 500 error) is the way you are sending data.
You specified the contentType as "application/json" .But you are sending the javascript object as it is. So your request payload will be sent as something like
Name=SomeNameValue&Url=SomeUrlValue And with your current server code, the model binder cannot map it to an object of SettingProfile.
The solution is to send the stringified version of your javascript object. You may use the JSON.stringify method to do so.
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
contentType: "application/json; charset=utf-8",
method: "POST",
data: JSON.stringify(settingProfile)
}).done(function (response) {
alert("Profile saved successfully");
};
Now your client code will send a request payload like
{"Name":"SomeName","Url":"SomeUrl"}
and model binder will be able to map it properly.
If it is simple data, you can simply not mention a custom contentType and send the js object as it is. jQuery will use the default contentType value which is "application/x-www-form-urlencoded" and model binding will work.
So the below code will work fine.
jQuery.ajax({
url: "#Url.Action("Create", "SettingProfile")",
method: "POST",
data:settingProfile
})
You may also check the browser's network tab and see the response coming back from the server for that ajax call. If an exception happens, you can see the details there.
I've tried researching this but I can't find anything anywhere that's helpful or any relevant answers.
For my angular controller I have:
app.controller('AdminCtrl', function ($scope, $http) {
$scope.data = {
Name: '',
Password: ''
},
$scope.login = function (data) {
$http({
method: 'POST',
url: '/login/postlogin',
data: JSON.stringify({
data: $scope.data
}),
contentType: "application/json",
dataType: "json"
}).success(function (data) {
alert('success!');
}).error(function (error) {
alert(error.message);
})
}
});
For my c# controller I have a very basic setup:
[HttpPost]
public string PostLogin(string data) {
return string.Empty;
}
My issue is that inside of the c# controller, data is always null. Does anyone know how to pass the name and password params over to the c# controller? I know for a fact that I am recieving values for them from my textboxes that I have. I am also recieving the success message/alert.Thanks!
Data is not a string. In c# make an object with Name and Password as properties and have this:
[HttpPost]
public string PostLogin(Data data) {
Edit in response to comment:
Yes you need to include the namespace that Data is in in your controller. You could try putting the Data object directly above.
public class Data
{
public string Name { get; set; }
public string Password { get; set; }
}
For clarity, data should not be stringified, instead just sent as data: data
Just send $scope.data directly, dont stringify it. Then modify your PostLogin method so it accepts two strings, one called Name and one called Password.
I'm trying to POST some data to my web API using jQuery's AJAX function, but MVC doesn't serialise it successfully on the other end - it simply leaves the default values for the model. Some seemingly useful answers don't seem to solve my problem, and its not at all complicated, so I'm not sure what's going wrong.
My model:
public class SavedLevel
{
public int ID { get; set; }
public string Data { get; set; }
}
AJAX request:
var postData= {};
postData.ID = 12;
postData.Data = "hello";
$.ajax(
{
url: "api/level/post",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({data : postData}),
dataType: "json",
success: function (data)
{
//Something
},
error: function (jqXHR, textStatus, errorThrown)
{
//Something
}
});
The API controller.
[Route("api/level/post")]
[HttpPost]
public HttpResponseMessage PostLevel(SavedLevel data)
{
Debug.WriteLine("id : " + data.ID);
Debug.WriteLine("data : " + data.Data);
return new HttpResponseMessage(HttpStatusCode.OK);
}
The request body.
{"data":{"ID":12,"Data":"hello"}}
The Debug console outputs 0 and "" instead of the expected 12 and "hello".
Thanks for reading.
Note: I tried using a dynamic object and doesn't work at all - I can't even check if it's null.
Change your data to:
data: JSON.stringify(postData),
So the request body becomes:
{"ID":12,"Data":"hello"}
The C# is expecting the SavedLevel object, but you are sending the object wrapped in another object.