Ajax call to MVC method results in 404 - javascript

I'm developing a drill down chart using Canvasjs and MVC5. I have a Controller called JsonController that contains several Tasks that return Json. They're all quite similar, but accept more arguments as the layers increase. Layer 0 is the default layer.
[HttpPost]
public async Task<IActionResult> GetLayer0(string datePassedIn)
{
string orgCode = User.Identity.GetOrgCode();
DateTime? processDate;
DateTime defaultDate = DateTime.Today.AddDays(-1); //default yesterday
try
{
processDate = DateTime.ParseExact(datePassedIn, inputDateFormat, cultureProvider);
}
catch (FormatException ex)
{
_logger.LogError(ex, "Error formatting date {datePassedIn} did not match {inputDateFormat}. using default date {defaultDate}", null);
processDate = defaultDate;
}
List<DataPoint> dataPoints = new List<DataPoint>();
IEnumerable<EventTypeLayer1> results = await _context.EventTypeLayer1Results
.FromSql($"usp_dd_EventType_0 #p0, #p1", orgCode, processDate)
.ToListAsync();
foreach (EventTypeLayer1 result in results)
{
dataPoints.Add(new DataPoint(result.Value, result.Title, result.Colour));
}
return Json(dataPoints);
}
In the javascript, the ajax calls are managed with an array
var ajaxOptions = [
{
url: "~/Views/Json/GetLayer0",
data: {
layer: 0,
processDate: encodeURIComponent(formatDateInput(param.processDate)),
orgCode: encodeURIComponent(param.orgCode)
},
callback : handleLayer0
},
{
url: "~/Views/Json/GetLayer1",
data: {
layer: 1,
processDate: encodeURIComponent(formatDateInput(param.processDate)),
orgCode: encodeURIComponent(param.orgCode),
eventType: encodeURIComponent(param.eventType)
},
callback : handleLayer1
},
{
url: "~/Views/Json/GetLayer2",
data: {
layer: 2,
processDate: encodeURIComponent(formatDateInput(param.processDate)),
orgCode: encodeURIComponent(param.orgCode),
eventType: encodeURIComponent(param.eventType),
driverId: encodeURIComponent(param.driverId)
},
callback : handleLayer2
}
];
function doAjax( layerIndex) {
$.ajax({
type: "POST",
cache: false,
dataType: "json",
url: ajaxOptions[layerIndex].url,
data: ajaxOptions[layerIndex].data,
success: function (serverResponse) {
//once a successful response has been received,
//no HTTP error or timeout reached,
//run the callback for this request
ajaxOptions[layerIndex].callback(serverResponse);
},
complete : function () {
//note that the "success" callback will fire
//before the "complete" callback
console.log("Ajax call complete");
}
});
}
When the ajax fires, I'm getting Errors
https://localhost:44388/~/Views/Json/GetLayer0 error 404
https://localhost:44388/Json/GetLayer0 error 405
#Url.Action("GetLayer0", "JsonController") renders blank
I'm a bit confused. What should I do?
Edit: Here's the actual AJAX call
function doAjax( layerIndex) {
$.ajax({
type: "POST",
cache: false,
dataType: "json",
url: ajaxOptions[layerIndex].url,
data: ajaxOptions[layerIndex].data,
success: function (serverResponse) {
//once a successful response has been received,
//no HTTP error or timeout reached,
//run the callback for this request
ajaxOptions[layerIndex].callback(serverResponse);
},
complete : function () {
//note that the "success" callback will fire
//before the "complete" callback
console.log("Ajax call complete");
}
});
}

You are callig view urls instead of controller functions
It should be like
{
url: "/youcontrollername/GetLayer0",
data: {
layer: 0,
processDate: encodeURIComponent(formatDateInput(param.processDate)),
orgCode: encodeURIComponent(param.orgCode)
},
callback : handleLayer0
},

Related

Problem: pass some parameters in ajax call (post)

I have 2 functions: one to add and another to delete. I would like to reuse the same ajax call to send the parameters that are added or deleted. How can I optimize my function?
Here is my code at the moment
jQuery(document).ready(function () {
function ajaxCall(action, callback) {
jQuery.ajax('/index.php', {
type: 'POST',
dataType: 'json',
data: {
option: 'quotes',
view: 'request',
task: action,
format: 'raw',
tmpl: 'component'
},
success: function (response) {
if (response.error == true) {
alert(response.errors.join('\n'));
}
else if (response.status == "DONE") {
callback(false);
}
},
error: function (xhr) {
console.log("Error: ", JSON.stringify(xhr));
callback(true);
}
});
}
jQuery('#ajax_add').click(function (event) {
event.stopPropagation();
var id = jQuery('#id').val();
var price = jQuery('#price').val();
//I want to send two variables: id, price
ajaxCall("addData", function (error) {
if (error) {
alert("Error!.");
}
else {
alert("It's OK!");
}
});
});
});
The function to delete is similar to "addData" function, it also calls "ajaxCall" and will send parameters to remove.
I'm blocked and I do not know how to solve it, I hope you can give me some help, thanks
You could add a new argument to the ajaxCall function and send the parameters as an object them merge them with the data you've in the function like :
function ajaxCall(action, params, callback) {
Then in the ajax call :
jQuery.ajax('/index.php', {
type: 'POST',
dataType: 'json',
data: $.extend(params, {
option: 'quotes',
view: 'request',
task: action,
format: 'raw',
tmpl: 'component'
}),
...
The call inside the event will be like :
ajaxCall("addData", {id: id, price: price}, function (error) {

Show waiting dialog on synchronous ajax

I want to show a waiting dialog while a synchronous ajax is made.
I using a Smart Wizard, to change between step one to step to i have to validate some data to do that i have to make 3 ajax call one after the other and while this is done i want to show a waiting dialog. This is what I'm doing.
if (indexes.fromStep==1) {
res=false;
var validatorResult = validator.checkAll($("#install_modbus_form"))
if (validatorResult) {
$("#modal_loader").modal()
$.ajax({
type: "post",
url: url1,
async: false,
dataType: "json",
data:{
data
},
success: function(response)
{
if (response.success)
{
$.ajax({
type: "post",
url: url2,
async: false,
dataType: "json",
data:{
data
},
success: function(response)
{
if (response.success)
{
$.ajax({
type: "post",
url: url3,
async: false,
dataType: "json",
data:{
data
},
success: function(response)
{
if (response.success)
{
//make magic here
res=true;
}
},
failure:function()
{
waitingDialog.hide()
res=false
},
error:function(a,b,c) {
waitingDialog.hide()
res=false
}
)
}
},
failure:function()
{
waitingDialog.hide()
res=false
},
error:function(a,b,c) {
waitingDialog.hide()
res=false
}
)
}
},
failure:function()
{
waitingDialog.hide()
res=false
},
error:function(a,b,c) {
waitingDialog.hide()
res=false
}
)
$("#modal_loader").modal('hide')
return res;//if true change step
}
}
I have trie use beforeSend to show the waiting dialog, also i have trie to use setTimeout but the waiting dialog is not show and the smart wizard dont go forward
Hope you can help, Im new in jquery.
Sorry for the bad english
On the assumption that you are using jQuery-Smart-Wizard, the solution lies in :
the construction of your onLeaveStep event handler, and (or including)
a modified version of the validation code shown in the question.
Fortunately, even though the plugin does not natively support asynchronism, it is fairly simple to make it do so. Essentially, what you need to do is :
to return false from the onLeaveStep callback,
to establish a promise which fulfills on successful validation, or rejects on failure,
to call .smartWizard('goForward') from the promise's success handler,
to call .smartWizard('showError') from the promise's error handler.
Based on smartWizard's ReadMe.md, here's a framework for performing synchronous and asynchronous validations :
$(document).ready(function() {
var waitingDialog = $('#whatever'); // ???
// Smart Wizard
$('#wizard').smartWizard({
onLeaveStep: leaveAStepCallback,
onFinish: onFinishCallback
});
function leaveAStepCallback(obj, context) {
alert("Leaving step " + context.fromStep + " to go to step " + context.toStep);
var returnValue;
switch(context.fromStep) {
case 1: // asynchronous
if (validator.checkAll($("#install_modbus_form"))) {
$("#modal_loader").modal();
waitingDialog.show();
validateStep1() // validateStep1() returns a promise
.then(function() {
// You will arrive here only if all three ajax calls were successful and all three responded with a truthy `response.success`.
$('#wizard').smartWizard('goForward'); // advance to next step
}, function(e) {
// You will arrive here on validation failure
$('#wizard').smartWizard('showError', e.message); // something went wrong
}).always(function() {
// You will arrive here on validation success or failure
waitingDialog.hide(); // the waiting is over
$("#modal_loader").modal('hide'); // ???
});
} else {
$('#wizard').smartWizard('showError', 'validator.checkAll() failed');
}
returnValue = false; // *must* return false to remain at step 1. If validation is successful, `.smartWizard('goForward')` will be executed later (see above).
break;
case 2: // synchronous
returnValue = validateStep2(); // validateStep2() returns true of false
break;
case 3:
...
break;
}
return returnValue; // true or false
}
// And here's the all-important `validateStep1()` :
function validateStep1() {
var sequence = [
{ url: 'url/1', data: {...} },
{ url: 'url/2', data: {...} },
{ url: 'url/3', data: {...} }
];
return sequence.reduce(function(promise, item, i) {
return promise.then(function() {
return $.ajax({
'type': 'post',
'url': item.url,
'dataType': 'json',
'data': item.data
}).then(function(response, textStatus, jqXHR) {
return response.success ? response : $.Deferred().reject(jqXHR, 'response.success not truthy at validation stage ' + i); // note: need to mimic jQuery.ajax's error signature.
});
});
}, $.when()) // starter promise for the reduction
.then(null, function(jqXHR, textStatus, errorThrown) {
return $.Deferred().reject(new Error(textStatus || errorThrown));
});
}
function validateStep2() {
// if validation here is synchronous, then return true of false
if(....) {
return true;
} else {
return false;
}
}
function validateStep3() {
...
}
// etc.
function onFinishCallback(objs, context) {
if(validateAllSteps()) {
$('form').submit();
}
}
function validateAllSteps() {
var isStepValid = true;
// all step validation logic
return isStepValid;
}
});
Notes :
the branching logic is in the onLeaveStep callback.
validateStep1() uses a chained promise pattern to sequence the three ajax calls.
if validateAllSteps() needs to repeat the step1 validation, then you will need call validateStep1().then(...) again, or chain from a previously cached promise.
As you can see, some aspects above are incomplete so there's still some work to do.

Ajax wont call MVC controller method

I have an AJAX function in my javascript to call my controller method. When I run the AJAX function (on a button click) it doesn't hit my break points in my method. It all runs both the success: and error:. What do I need to change to make it actually send the value from $CSV.text to my controller method?
JAVASCRIPT:
// Convert JSON to CSV & Display CSV
$CSV.text(ConvertToCSV(JSON.stringify(data)));
$.ajax({
url: '#Url.Action("EditFence", "Configuration")',
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: { value : $CSV.text() },
success: function(response){
alert(response.responseText);
},
error: function(response){
alert(response.responseText);
}
});
CONTROLLER:
[HttpPost]
public ActionResult EditFence(string value)
{
try
{
WriteNewFenceFile(value);
Response.StatusCode = (int)HttpStatusCode.OK;
var obj = new
{
success = true,
responseText = "Zones have been saved."
};
return Json(obj, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
var obj = new
{
success = false,
responseText = "Zone save encountered a problem."
};
return Json(obj, JsonRequestBehavior.AllowGet);
}
}
RESULT
You should change the data you POST to your controller and the Action you POST to:
data: { value = $CSV.text() }
url: '#Url.Action("EditFence", "Configuration")'
The $CSV is possible a jquery Object related to an html element. You need to read it's text property and pass this as data, instead of the jQuery object.
Doing the above changes you would achieve to make the correct POST. However, there is another issue, regarding your Controller. You Controller does not respond to the AJAX call after doing his work but issues a redirection.
Update
it would be helpful for you to tell me how the ActionResult should
look, in terms of a return that doesn't leave the current view but
rather just passes back that it was successful.
The Action to which you POST should be refactored like below. As you see we use a try/catch, in order to capture any exception. If not any exception is thrown, we assume that everything went ok. Otherwise, something wrong happened. In the happy case we return a response with a successful message, while in the bad case we return a response with a failure message.
[HttpPost]
public ActionResult EditFence(string value)
{
try
{
WriteNewFenceFile(value);
Response.StatusCode = (int)HttpStatusCode.OK;
var obj = new
{
success = true,
responseText= "Zones have been saved."
};
return Json(obj, JsonRequestBehavior.AllowGet));
}
catch(Exception ex)
{
// log the Exception...
var obj = new
{
success = false,
responseText= "Zone save encountered a problem."
};
return Json(obj, JsonRequestBehavior.AllowGet));
}
}
Doing this refactor, you can utilize it in the client as below:
$CSV.text(ConvertToCSV(JSON.stringify(data)));
$.ajax({
url: '#Url.Action("EditFence", "Configuration")',
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: { value = JSON.stringify($CSV.text()) },
success: function(response){
alert(response.responseText);
},
error: function(response){
alert(response.responseText);
}
});
If your javascript is actually in a JS file and not a CSHTML file, then this will be emitted as a string literal:
#Url.Action("EditFile", "Configuration")
Html Helpers don't work in JS files... so you'll need to point to an actual url, like '/configuration/editfile'
Also, it looks like you're posting to a method called EditFile, but the name of your method in the controller code snippet is EditFence, so that will obviously be an issue too.
you dont need to add contentType the default application/x-www-form-urlencoded will work because it looks like you have a large csv string. So your code should be like this example
$(document).ready(function() {
// Create Object
var items = [
{ name: "Item 1", color: "Green", size: "X-Large" },
{ name: "Item 2", color: "Green", size: "X-Large" },
{ name: "Item 3", color: "Green", size: "X-Large" }
];
// Convert Object to JSON
var $CSV = $('#csv');
$CSV.text(ConvertToCSV(JSON.stringify(items)));
$.ajax({
url: '#Url.Action("EditFence", "Configuration")',
type: "POST",
dataType: "json",
data: {value:$CSV.text()},
success: function(response) {
alert(response.responseText);
},
error: function(response) {
alert(response.responseText);
}
});
Your problem is on these lines:
success: alert("Zones have been saved."),
error: alert("Zone save encountered a problem.")
This effectively running both functions immediately and sets the return values of these functions to the success and error properties. Try using an anonymous callback function.
success: function() {
alert("Zones have been saved.");
},
error: function() {
alert("Zone save encountered a problem.")
}

How to put information from one Ajax request into another

I have this code to get two parts of data, these two ajax requests get data from php class.
{
xtype: 'button',
formBind: true,
id: 'saveLicenceBtn',
text: 'Save',
listeners: {
click: function (c) {
//first ajax request
var d = Ext.Ajax.request({
url: 'system/index.php',
params: {
class: 'generatemultiple',
method: 'getSession'
},
success: function (response) {
var object = Ext.decode(response.responseText, true);
console.log(object);
},
failure: function (response) {
var object = Ext.decode(response.responseText, true);
console.log(object);
}
});
//second ajax request
Ext.Ajax.request({
url: 'system/index.php',
method: 'POST',
params: {
class: 'generatemultiple',
method: 'add',
data: Ext.encode({
count: Ext.getCmp('count').getValue(),
start_date: Ext.getCmp('startdateTextField').getValue(),
end_date: Ext.getCmp('enddateTextField').getValue(),
duration: Ext.getCmp('durationTextField').getValue(),
expiry_date: Ext.getCmp('expirydateTextField').getValue(),
product_id: Ext.getCmp('productidTextField').getValue(),
company_id: Ext.getCmp('companyidtf').getValue(),
token: d
})
},
success: function (response) {
Ext.MessageBox.alert('Status', 'Success');
Ext.getStore('LicenseStore').reload();
Ext.getStore('LicenseAllStore').reload();
Ext.getStore('LicenseFeaturesStore').reload();
Ext.getStore('HardwareStore').reload();
Ext.getStore('DropdownLicenseStore').reload();
Ext.getStore('GridHardwareStore').reload();
Ext.getStore('HardwareAllStore').reload();
Ext.getCmp('addLicenseWindow').close();
},
failure: function (response) {
Ext.MessageBox.alert('Status', 'Failure');
Ext.getCmp('addLicenseWindow').close();
}
});
}
}
}
The first ajax request gets a session variable from the webpage, and the second ajax request sends this token with the ajax request. What I need to know is how do I do what is shown here without getting this error.
Uncaught RangeError: Maximum call stack size exceeded
I know what the error means and I am aware of the reason why its occuring, but i cant find a solution. it keeps occuring because I have two functions calling each other so it errors in the web console.
What i tried alternatively was this
ONCLICK
click: function (c) {
Ext.Ajax.request({
url: 'system/index.php',
method: 'POST',
params: {
class: 'generatemultiple',
method: 'add',
data: Ext.encode({
count: Ext.getCmp('count').getValue(),
start_date: Ext.getCmp('startdateTextField').getValue(),
end_date: Ext.getCmp('enddateTextField').getValue(),
duration: Ext.getCmp('durationTextField').getValue(),
expiry_date: Ext.getCmp('expirydateTextField').getValue(),
product_id: Ext.getCmp('productidTextField').getValue(),
company_id: Ext.getCmp('companyidtf').getValue(),
token: '<?php echo $_SESSION["user_id"] ?>'
})
},
success: function (response) {
Ext.MessageBox.alert('Status', 'Success');
Ext.getStore('LicenseStore').reload();
Ext.getStore('LicenseAllStore').reload();
Ext.getStore('LicenseFeaturesStore').reload();
Ext.getStore('HardwareStore').reload();
Ext.getStore('DropdownLicenseStore').reload();
Ext.getStore('GridHardwareStore').reload();
Ext.getStore('HardwareAllStore').reload();
Ext.getCmp('addLicenseWindow').close();
},
failure: function (response) {
Ext.MessageBox.alert('Status', 'Failure');
Ext.getCmp('addLicenseWindow').close();
}
});
}
forgive the indentation. End goal of what I am trying to do is send the session variable from the php with this ajax request
You can make first ajax request as a synchronous call So that second ajax request will wait for the first ajax request to complete. Set async: false in the first request.

.NET MVC JSON Post Call response does not hit complete or success

I am new to .NET MVC so please bear with me.
I wrote a function that gets triggered when there is a blur action on the textarea control:
function extractURLInfo(url) {
$.ajax({
url: "/Popup/Url",
type: "POST",
data: { url: url },
complete: function (data) {
alert(data);
},
success: function (data) {
alert(data);
},
async: true
})
.done(function (r) {
$("#url-extracts").html(r);
});
}
jQuery(document).ready(function ($) {
$("#input-post-url").blur(function () {
extractURLInfo(this.value);
});
});
This works fine and will hit the controller:
[HttpPost]
public ActionResult Url(string url)
{
UrlCrawler crawler = new UrlCrawler();
if (crawler.IsValidUrl(url))
{
MasterModel model = new MasterModel();
model.NewPostModel = new NewPostModel();
return PartialView("~/Views/Shared/Partials/_ModalURLPartial.cshtml", model);
}
else
{
return Json(new { valid = false, message = "This URL is not valid." }, JsonRequestBehavior.AllowGet);
}
}
I get the intended results if the URL is valid; it will return a partialview to the .done() function and I just display it in code. However, if the URL is not valid i want it to hit either complete, success, or done (I have been playing around to see which it will hit but no luck!) and do something with the returned data. I had it at some point trigger either complete or success but the data was 'undefined'. Can someone help me out on this?
Thanks!
In both cases your controller action is returning 200 status code, so it's gonna hit your success callback:
$.ajax({
url: "/Popup/Url",
type: "POST",
data: { url: url },
success: function (data) {
if (data.message) {
// Your controller action return a JSON result with an error message
// => display that message to the user
alert(data.message);
} else {
// Your controller action returned a text/html partial view
// => inject this partial to the desired portion of your DOM
$('#url-extracts').html(data);
}
}
});
But of course a much better and semantically correct approach is to set the proper status code when errors occur instead of just returning some 200 status code:
[HttpPost]
public ActionResult Url(string url)
{
UrlCrawler crawler = new UrlCrawler();
if (crawler.IsValidUrl(url))
{
MasterModel model = new MasterModel();
model.NewPostModel = new NewPostModel();
return PartialView("~/Views/Shared/Partials/_ModalURLPartial.cshtml", model);
}
else
{
Response.StatusCode = 400;
Response.TrySkipIisCustomErrors = true;
return Json(new { valid = false, message = "This URL is not valid." }, JsonRequestBehavior.AllowGet);
}
}
and then in your AJAX call you would handle those cases appropriately:
$.ajax({
url: "/Popup/Url",
type: "POST",
data: { url: url },
success: function (data) {
$('#url-extracts').html(data);
},
error: function(xhr) {
if (xhr.status == 400) {
// The server returned Bad Request status code
// => we could parse the JSON result
var data = JSON.parse(xhr.responseText);
// and display the error message to the user
alert(data.message);
}
}
});
Also don't forget that you have some standard way of returning your error messages you could subscribe to a global .ajaxError() handler in jQuery instead of placing this code in all your AJAX requests.

Categories

Resources