MVC5 action that can return either View(model) or json - json

I have a following problem:
I got an EDIT page, it contains some inputs and so on - nothing unusual. On submit button click I have to validate the form on server and do one of the following actions:
return the model if there are errors
return some info saying it's all good
Why? Because I need to display a bootstrap modal window when everything's ok. So what I did is (simplifying):
[HttpPost]
public ActionResult EditData(DataViewModel model)
{
...
if(!ModelState.IsValid)
return View(model);
return Json(new { success = true, message = "all good" }, JsonRequestBehavior.AllowGet);
}
What is the problem? I'm posting the form with javascript and waiting for the answer like that:
{
$(document).on('click', '#submitButton', function () {
$.ajax({
dataType: "json",
url: "/Controller/EditData",
type: "POST",
data: $('#submitForm').serialize()
}).done(function (data) {
console.log(data.success + ' ' + data.message);
if (data.success) {
$('#modalBody').text(data.message);
$('#modal-window-add').modal('show');
}
});
});
}
So as you can see it does display the modal window but it doesn't do anything when a model comes in. In the latter situation completely nothing happens although the response comes.
Is there any other option to solve my problem? Generally I HAVE to display that modal window on successful edit and on modal closing I need to redirect to another page - this is easy. I tried including the model inside the json but it didn't work and it seems like a lot of work to check all the fields manually and then displaying these red errors under inputs that didn't pass validation. I discussed it with my experienced friends but they don't have any MVC based solution. Any idea?
Best regards,
Daniel

User Ajax For to do that work
#{
var ajaxOptions = new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "modal", OnBegin = "onAjaxBegin", OnComplete = "OnAjaxComplete", OnSuccess = "OnAjaxSuccess" };
}
#using (Ajax.BeginForm("Controler", "EditData", ajaxOptions, new { enctype = "multipart/form-data" }))
{
//write html here with model values
}
COntroller Method
[HttpPost]
public ActionResult EditData(DataViewModel model)
{
if(!ModelState.IsValid)
return View(model);
}
Javascript
function OnAjaxSuccess()
{
show hide model popup
}

$.ajax have a dataType option (The type of data that you're expecting back from the server.)
In $.ajax function you added option dataType: "json" that means you expected json back from server so you will get only json not other dataType like xml, html, text, script
In your case you want to return json or html depending on model is valid or not for that you can remove dateType option from $.ajax function, because If none is specified, jQuery will try to infer it based on the MIME type of the response (an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string).

Related

Ajax call not working when trying to send & return raw html

I am using Ajax call to invoke C# method that is processing user paste input by removing unwanted html tags and attributes.
When I paste in content with html formatting (tags & attributes), my Ajax call doesn't work. Can you please advise how Ajax parameters should be defined for such scenario (send raw html to server side code and get raw html returned)?
view:
#(Html.Kendo().Editor()
.Name("myEditor")
.PasteCleanup(p => p.Custom("myPasteCleanUp")
)
script:
function myPasteCleanUp(input) {
var response = null;
if (input != null) {
$.ajax({
type: "POST",
url: '/Home/htmlTagCleanUp',
data: { userInput: input },
async: false,
success: function (response) {
input = response;
},
});
}
return input;
}
controller:
[HttpPost]
[AllowAnonymous]
public ActionResult htmlTagCleanUp(string userInput)
{
userInput = userInput;
return Content(userInput, "text/html");
}
It turned out the missing part was sanitizing HTML:
var = document.createElement('div');
element.innerText = html;
var sanitizedHTML = element.innerHTML;
The thing that is preventing your AJAX call is because you have added if conditions:
if (html != null) {
}
Can you tell me where the html variable is defined. Nonetheless, I think you are trying to check the input null values and if we replace the html by input variable on the if the condition it should work:
if (input != null) {
}
I guess the issue is that MVC considers data with tags as a bad request.
Therefore would suggest you try it above the action method in your controller:
[ValidateInput(false)]
Looks like you need to add the [ValidateInput(false)] attribute to your method as it may treat as an XSS attack.
Ref: ValidateInput(false) vs AllowHtml

ASP MVC Areas and JSON POST

I have a project with areas and would like to post a view model as JSON to a controller method.
This is what I have, with performance being generated in the default area and passed to the view in area SeatSelection:
$("#addToCartButton").click(function () {
var json = #Html.Raw(Json.Encode(performance));
$.ajax({
url: 'https://#(Request.Url.Host)/SeatSelection/Home/AddToCart',
type: 'POST',
dataType: 'json',
data: json,
contentType: 'application/json; charset=utf-8',
success: function (data) {
alert(data);
}
});
});
And the action method for testing:
[System.Web.Http.Route("SeatSelection_AddToCart")]
[System.Web.Http.HttpPost]
public JsonResult AddToCart(PerformanceViewModel performance)
{
return Json(performance.Name);
}
I created the following route:
context.MapRoute(
"SeatSelection_AddToCart",
"SeatSelection/Home/AddToCart",
new { action = "AddToCart", controller = "Home", id = UrlParameter.Optional },
namespaces: new string[] { "myProject.Areas.SeatSelection.Controllers" }
);
But all I get is a internal server error 500. I also tried to use [FromBody] and setting a breakpoint to the method, but it is not invoked. I can't figure out what's wrong or missing, please help.
UPDATE
This is the json / performance:
PerformanceID=00000000-0000-0000-0000-000000000000&Name=Performance+15&StartDate=%2FDate(1360364400000)%2F&EndDate=%2FDate(1500328800000)%2F&LatestDateBookable=%2FDate(1450911600000)%2F&Organizer=Organizer+15&Location=Location+15&Availability=75&IsFull=false&IsBookable=true&HasPrice=true&BookableSeats=11&BookedSeats=94&Description=Description+of+Performance+15&Price=443
I found an error: "invalid json primitive: performanceid"
First of all, I would recommend you to use #Url.Action helper method instead of generating url like this: https://#(Request.Url.Host)/SeatSelection/Home/AddToCart.
Secondly, always validate params which comes from the browser. return Json(performance.Name) looks suspicious. What is performance will be null? This might be a problem of your internal server error 500.
If this is not a problem then try to send string instead of JSON to the server and validate and parse JSON on the server side.
You can use Url.Action method like this. I suppose SeatSelection is an area in your project.
$.ajax({
url: '#Url.Action("AddToCart", "Home", new { Area = "SeatSelection"})',

Ajax call to JsonResult in Controller fails with 404 error, "resource not found"

Here is my ajax code in my "ManageUserRoles.cshtml":
//HIT THE DATABASE FOR USERNAME GIVING IT THIS USERNAME
function isUserValid(thisUser) {
$.ajax({
url: "/Roles/isUserValid/" + thisUser,
type: 'POST',
success: handleResultResponseUserName,
error: function (xhr) { alert("Error..."); }
});
}
//handles data back from ajax call
//RESET INPUT IF NO USER IF FOUND IN USER'S TABLE
function handleResultResponseUserName(ResponseObject) {
if (ResponseObject == "no user with this number") {
$('#frmGetRoles').find('input[name="UserName"]').val(null);
}
else {
//DO NOTHING
}
}
Here is my JsonResult in my RolesController:
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult isUserValid(string username)
{
var name = string.Empty;
var CheckUserExists = (from c in _db.AspNetUsers
where (c.UserName.Equals(username))
select c);
var results = new JsonResult();
if (CheckUserExists.Any())
{
name = CheckUserExists.First().UserName;
}
else
{
name = "no user with this name in database";
}
return Json(name, JsonRequestBehavior.DenyGet);
}
I've got almost exact code working in a different application and I cut and pasted into a new one where I am trying to use it for Roles management.
The json reference is there, and in web.config. But when I put a breakpoint inside the JsonResult, it never stops there and an error is returned from the client javascript (404 resource not found). There is no other json stuff being used in this new app . . .yet.
I can hit f5 and it returns:
http://localhost/StoreMasterSecure/Roles/ManageUserRoles
which is the view that has the button that runs the ajax. It all gets to the ajax call and then nothing happens and Chrome Developer Tools console shows 404 error.
Even if I type in the URL the path, I get the resource not found 404 page:
http://localhost/StoreMaster/Roles/isValidUser/xxxx#xxx.com
(isValidUser is the JsonResult in the controller, in same controller that ManageUserRoles ActionResult exists and works)
To ensure you url's are correctly generated, use the Url.Action() method and pass the data using the ajax data option. Change your ajax to
$.ajax({
url: '#Url.Action("isUserValid", "Roles")', // change this
data: { username: thisUser }, // add this
type: 'POST',
success: handleResultResponseUserName,
error: function (xhr) { alert("Error..."); }
});
You also need to remove the [ValidateAntiForgeryToken] attibute from your controller method since you not passing the token.
Side note: MVC comes with a RemoteAttribute to handle this scenario. Refer How to: Implement Remote Validation in ASP.NET MVC which means you do not need your script.

viewbag data is empty in $.ajax

Iam using asp.net mvc4 and facing some problem in accessing viewbag.price.
This is what i am doing:-
[HttpPost]
public ActionResult FillModel(int id)
{
var vehModel = db.Vehicle_Model.Where(vehMod => vehMod.MakeID == id).ToList().Select(vehMod => new SelectListItem() { Text = vehMod.Model, Value = vehMod.pkfModelID.ToString() });
ViewBag.Price = 100;
return Json(vehModel, JsonRequestBehavior.AllowGet);
}
i am calling above using below:-
$.ajax({
url: '#Url.Action("FillModel","Waranty")',
type: 'post',
data: { id: id },
dataType: 'json',
success: function (data) {
$('#ddModel').empty();
$.each(data, function (index, val) {
var optionTag = $('<option></option>');
$(optionTag).val(val.Value).text(val.Text);
$('#ddModel').append(optionTag);
});
var a = '#ViewBag.Price';
},
error: function () {
alert('Error');
}
});
But i am not able to access ViewBag.Price.
Anyone know the reason??
thanks
The reason you aren't able to access items from the ViewBag inside your ajax success function is because the view that contains your script has already been rendered by the Razor view engine, effectively setting the variable a to whatever the value of #ViewBag.Price was at the time the page was rendered.
Looking at the process flow might be helpful:
(1) The request comes in for the view that has your script fragment in it.
(2) The controller method that returns your view is called.
(3) The Razor view engine goes through the view and replaces any references to #ViewBag.Price in your view with the actual value of ViewBag.Price. Assuming ViewBag.Price doesn't have a value yet, the success function in your script is now
success: function (data) {
$('#ddModel').empty();
$.each(data, function (index, val) {
var optionTag = $('<option></option>');
$(optionTag).val(val.Value).text(val.Text);
$('#ddModel').append(optionTag);
});
var a = '';
}
(4) The rendered html gets sent to the client
(5) Your ajax request gets triggered
(6) On success, a gets set to the empty string.
As you had mentioned in the comments of your question, the solution to this problem is to include a in the Json object returned by your action method, and access it using data.a in your script. The return line would look like
return Json(new {
model = vehModel,
a = Price
});
Keep in mind that if you do this, you'll have to access model data in your ajax success function with data.model.Field. Also, you shouldn't need to specify the JsonRequestBehavior.AllowGet option, since your method only responds to posts and your ajax request is a post.

How to synchronize returning function values in jQuery?

I have written this function in jQuery:
function checkAvailability(value) {
var result = true;
$.getJSON("registration/availability", { username: value }, function(availability) {
if (!availability)
result = false;
alert("in getJSON: " + result);
});
alert(result);
return result;
}
I have got alert from 'getJSON' after this second. Why has it happened this way?
I have Spring MVC project and Controller method which checks username availability. Controller method works properly. But I receive final result too late. How can I synchronize it to return properly value in my function?
EDIT
I am using this function in jQuery validate. I have extracted checkAvailability() function during my test.
$.validator.addMethod("checkAvailability", function(value, element, param) {
var das = checkAvailability(value);
return das;
}, jQuery.format("Someone already has that username. Please try another one."));
And this is my form validate:
$(".form").validate({
rules: {
username: {
checkAvailability: true
},
....
},
messages: {
}
});
EDIT 2
This is my Controller method. It returns boolean value. If username was taken it would return false value.
#RequestMapping(value="/registration/availability", method = RequestMethod.POST)
public #ResponseBody boolean getAvailability(#RequestParam String username) {
List<User> users = getAllUsers();
for (User user : users) {
if (user.getUsername().equals(username)) {
return false;
}
}
return true;
}
Why does this behave this way?
$.getJSON is shorthand for making an AJAX request. The 'A' in ajax stands for asynchronous. Meaning, the javascript engine fires the getJSON call and then immediately executes the next lines, which is alert(result); return result;
The actual value as returned by the web service will be received by your code at a later point in time. The success function that you passed into getJSON will be called once the js engine receives the response from the server. As you can see, it is too late by that point.
Further reading: https://developer.mozilla.org/en/AJAX
What can I do to make this work?
That depends on your situation. Who is calling checkAvailabilty? If you post some code on how this function is being used, I can give examples with my suggestions.
Off the top of my head, you could either make use of jquery deferreds, nice article on the same. Or you could pass in a callback function that is executed from inside your success function.
EDIT:
http://docs.jquery.com/Plugins/Validation/Methods/remote#options
The serverside resource is called via $.ajax (XMLHttpRequest) and gets
a key/value pair, corresponding to the name of the validated element
and its value as a GET parameter. The response is evaluated as JSON
and must be true for valid elements, and can be any false, undefined
or null for invalid elements,
To get a real world idea, check the demo http://jquery.bassistance.de/validate/demo/captcha/
Open Firebug or the developer tools of your choice. Go to the tab that lets you see AJAX requests. Enter the captcha, submit. Check ajax request as listed in the developer tool. Notice the query string parameters. Notice the response. Its a simple 'true' or 'false'.
Not sure if this helps or not but you can use
var result;
$.ajax( {
url : "registration/availability",
data : data,
async : false //syhcrononous ajax request ;)
}).done(function(data) {
result = data;
});
for more info you can refer JQuery AJAX doc