When invoked with a jQuery $.ajax, what to return from the Action Method if things go ok? - c#

Here's my code:
[HttpPost]
public ActionResult VoteChampionStrongAgainst(string championStrong, string againstChampion)
{
int champStrongId = int.Parse(championStrong);
int againstChampId = int.Parse(againstChampion);
string ip = System.Web.HttpContext.Current.Request.UserHostAddress;
using (EfCounterPickRepository counterPickRepository = new EfCounterPickRepository())
{
var existingCounterPick = counterPickRepository.FindAll()
.SingleOrDefault(x => x.ChampionStrong == champStrongId && x.AgainstChampion == againstChampId);
//Does this counterpick combination already exist?
if (existingCounterPick != null)
{
//Has this user already voted?
var existingVote = counterPickRepository.FindVoteForUser(ip, existingCounterPick.CounterPickVoteId);
//He hasn't voted, add his vote history.
if (existingVote == null)
{
StrongCounterHistory history = new StrongCounterHistory();
history.IPAddress = ip;
history.VoteType = true;
history.StrongCounterPickVoteId = existingCounterPick.CounterPickVoteId;
counterPickRepository.AddStrongPickHistory(history);
counterPickRepository.SaveChanges();
//Add a total vote the pick.
existingCounterPick.TotalVotes++;
counterPickRepository.SaveChanges();
}
else
{
//Will use this to display an error message.
//How to return an "error" that jquery understands?
}
}
else //This combination doesn't exist. Create it.
{
//Create it....
StrongCounterPickVote newCounterPick = new StrongCounterPickVote();
newCounterPick.ChampionStrong = champStrongId;
newCounterPick.AgainstChampion = againstChampId;
newCounterPick.TotalVotes = 1;
counterPickRepository.CreateNewCounterPick(newCounterPick);
counterPickRepository.SaveChanges();
//Assign a vote history for that user.
StrongCounterHistory history = new StrongCounterHistory();
history.IPAddress = ip;
history.VoteType = true;
history.StrongCounterPickVoteId = newCounterPick.CounterPickVoteId;
counterPickRepository.AddStrongPickHistory(history);
counterPickRepository.SaveChanges();
}
return View();
}
}
Here's my jQuery code:
$(".pick .data .actions .btn-success").click(function () {
var champStrongId = $(this).data("champstrongid");
var againstChampId = $(this).data("againstchampid");
$.ajax({
type: 'POST',
url: "/Counterpicks/VoteChampionStrongAgainst",
data: { championStrong: champStrongId, againstChampion: againstChampId },
success: function () {
alert("Great success!");
},
error: function (e) {
alert("Something bad happened!");
console.log(e);
}
});
});
What do I need to return from my ActionMethod so the code execution enters success: if things went OK, or error: if things go wrong (for example, he already voted on this particular counter pick?

Servlet should answer a "200 OK" HTTP response.
Don't know about your 'View' api, but HttpServletResponse.setStatus(200) would do on the Java side. Don't forget, you can request the AJAX url manually in your browser to see what it is returning..

Here's some things I'd do...
public JsonResult VoteChampionStrongAgainst(string championStrong, string againstChampion) {
var success = true;
// Do all of your data stuff
return Json(new { success = success, error = 'Some error message'});
}
The JsonResult is a special ActionResult for returning Json. It automatically sets the correct headers for the browser. The Json() will use ASP.NET's built in serializer to serialize an anonymous object to return to the client.
Then with your jQuery code...
$.ajax({
type: 'POST',
url: "/Counterpicks/VoteChampionStrongAgainst",
data: { championStrong: champStrongId, againstChampion: againstChampId },
success: function (json) {
if (json.success) {
alert("Great success!");
}
else if(json.error && json.error.length) {
alert(json.error);
}
},
// This error is only for responses with codes other than a
// 200 back from the server.
error: function (e) {
alert("Something bad happened!");
console.log(e);
}
});
In order to have the error fire you'd have to return a different response code with Response.StatusCode = (int)HttpStatusCode.BadRequest;

You can return 500 internal server error if there are any errors on your server something like
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
Response.ContentType = "text/plain";
return Json(new { "internal error message"});

Related

Error handling: Display a blank input page if database row does not exist

I have an account settings page for a user to submit a video and give it a title to display on his/her profile. Once submitted, the video gets tied to the AccountInfoID and creates a row in my dbo.Spotlight database:
The only way to get that Video spotlight page to show above is if you manually enter a youtube video into the database that's tied to the accountinfoID since the page can then pull and display the information to fill the input boxes. If no row exists for the user, an error is thrown: System.NullReferenceException: Object reference not set to an instance of an object.
I was wondering what error handling I can do to check to see if a row in the spotlight database is found that has the user's account and if not, still display the page with blank input boxes. I commented above the line that I know causes the error.
public ActionResult CreatorSpotlight()
{
var model = new AccountSpotlightViewModel();
var userID = User.Identity.GetUserId();
var accountInfo = EntityDataAccess.GetAccountInfoByUserID(userID);
if(accountInfo != null && accountInfo.CreatorFL == false)
{
return RedirectToAction("Spotlight", "Account");
}
//the next line is what causes the page to throw an error since it may not find the userid in the spotlight database
model.Spotlight = EntityDataAccess.GetSpotlightByUserID(userID);
model.Spotlight.AccountInfo = null;
return View(model);
}
I submit through ajax:
function submitSpotlight()
{
var spotlight = new Object();
spotlight.AlbumName = $("#youtube-title").val();
spotlight.YouTubeURL = $("#video-spot-1").val();
$.ajax
({
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
url: "#Url.Content("~/Account/SubmitSpotlight/")",
data: JSON.stringify(spotlight),
success: function(data)
{
if(data == true)
{
location.reload();
}
},
error: function(jqXHR, textStatus, errorThrown)
{
//TODO: Indicate Error
}
});
}
Here's the JSON encode that's tied to the ajax request that stores the values into the dbo.spotlight
public JsonResult SubmitSpotlight(Spotlight spotlight)
{
try
{
var userID = User.Identity.GetUserId();
spotlight.AccountInfoID = EntityDataAccess.GetAccountInfoByUserID(userID).AccountInfoID;
if(!String.IsNullOrWhiteSpace(spotlight.YouTubeURL))
{
var videoKey = spotlight.YouTubeURL.Replace("https://youtu.be/", "");
spotlight.EmbedYouTubeURL = "https://www.youtube.com/embed/" + videoKey + "?showinfo=0";
spotlight.ThumbnailURL = "http://img.youtube.com/vi/" + videoKey + "/maxresdefault.jpg";
}
var _spotlight = EntityDataAccess.GetSpotlightByUserID(userID);
if(_spotlight == null)
{
EntityDataAccess.InsertSpotlight(spotlight);
}
else
{
spotlight.SpotlightID = _spotlight.SpotlightID;
EntityDataAccess.UpdateSpotlight(spotlight);
}
return Json(true);
}
catch(Exception ex)
{
return Json(ex);
}
}
Update added GetspotlightbyuserID
public static Spotlight GetSpotlightByUserID(string userID)
{
using(var Context = GetContext())
{
return Context.Spotlights.Include("AccountInfo").Where(x => x.AccountInfo.UserID == userID).FirstOrDefault();
}
}
The error could come from the returned value here
model.Spotlight = EntityDataAccess.GetSpotlightByUserID(userID);
Check the value of model.Spotlight for null before setting model.Spotlight.AccountInfo to null as follows;
model.Spotlight = EntityDataAccess.GetSpotlightByUserID(userID);
if (model.Spotlight!=null){
model.Spotlight.AccountInfo = null;
}
return View(model);

Error "Requested JSON parse failed" in downloading excel file

I have an action method which returns an excel file in return. I am calling that action method using ajax. I am getting Requested JSON parse failed.
$.ajax({
url: importUrl,
data: {
X: "12",
Y: "12",
Z: "12"
},
success: function (data) {
alert("S: "+data);
},
error: function (jqXHR, exception) {
var msg = '';
if (jqXHR.status === 0) {
msg = 'Not connect.\n Verify Network.';
} else if (jqXHR.status == 404) {
msg = 'Requested page not found. [404]';
} else if (jqXHR.status == 500) {
msg = 'Internal Server Error [500].';
} else if (exception === 'parsererror') {
msg = 'Requested JSON parse failed.';
} else if (exception === 'timeout') {
msg = 'Time out error.';
} else if (exception === 'abort') {
msg = 'Ajax request aborted.';
} else {
msg = 'Uncaught Error.\n' + jqXHR.responseText;
}
console.log(msg);
}
});
public ActionResult ExportReportToExcel(string X, string Y, string Z)
{
if (HttpContext.Request.UrlReferrer == null)
TempData["PDFPrevUrl"] = Url.RouteUrl("PageNotFound");
else if (TempData["PDFPrevUrl"] == null)
TempData["PDFPrevUrl"] = HttpContext.Request.UrlReferrer.PathAndQuery;
var customer = _authenticationService.CurrentCustomer;
if (customer == null)
return new LmsHttpUnauthorizedResult();
string filename = "Report";
try
{
XLWorkbook wb = new XLWorkbook(Server.MapPath(#"~/Content/CumulativePerformanceReportTemplate.xlsx"));
XElement userprogress = XElement.Load(Server.MapPath(#"~/Content/Export.xml")).Element("cumulativeperformancereport");
int datarow = int.Parse(userprogress.Element("T").Attribute("row").Value.Trim());
int datacol = int.Parse(userprogress.Element("T").Attribute("col").Value.Trim());
IXLWorksheet WS = wb.Worksheet(1);
WS.Cell(datarow, datacol).Value = customer.Name;
datarow = int.Parse(userprogress.Element("X").Attribute("row").Value.Trim());
datacol = int.Parse(userprogress.Element("X").Attribute("col").Value.Trim());
WS.Cell(datarow, datacol).Value = X;
datarow = int.Parse(userprogress.Element("Y").Attribute("row").Value.Trim());
datacol = int.Parse(userprogress.Element("Y").Attribute("col").Value.Trim());
WS.Cell(datarow, datacol).Value = Y;
datarow = int.Parse(userprogress.Element("Z").Attribute("row").Value.Trim());
datacol = int.Parse(userprogress.Element("Z").Attribute("col").Value.Trim());
WS.Cell(datarow, datacol).Value = Z;
Response.Clear();
Response.Buffer = true;
Response.Charset = "";
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment;filename=" + filename + "_Summary.xlsx");
using (MemoryStream MyMemoryStream = new MemoryStream())
{
wb.SaveAs(MyMemoryStream);
MyMemoryStream.WriteTo(Response.OutputStream);
Response.Flush();
Response.End();
}
return null;
}
catch (Exception ex)
{
return Redirect(TempData["PDFPrevUrl"].ToString());
}
}
Why am I getting this error?
"Requested JSON parse failed" indicating that AJAX call expects to get JSON data as returned value, but the controller action method returns other data type than JSON object.
By reviewing controller flow and omitting some non-relevant code, you will get this:
public ActionResult ExportReportToExcel(string X, string Y, string Z)
{
// other stuff
var customer = _authenticationService.CurrentCustomer;
if (customer == null)
return new LmsHttpUnauthorizedResult();
try
{
// other stuff
return null; // this returns null value instead of expected JSON
}
catch (Exception ex)
{
return Redirect(TempData["PDFPrevUrl"].ToString());
}
}
By default jQuery tries to infer dataType argument based on the MIME type of the response (xml, json, script or html, most recent default is JSON). Hence, you need to return a JSON object through these methods below:
// ContentResult
return Content("message_text", "application/json");
// JsonResult
return Json("message_text", JsonRequestBehavior.AllowGet);
If you want returning file by AJAX to download, you can use window.location or window.location.href to redirect:
$.ajax({
url: importUrl, // this should be refer to JsonResult action
data: {
X: "12",
Y: "12",
Z: "12"
},
success: function (data) {
// deal with data response here
window.location = downloadUrl; // redirect to FileResult action
},
error: function (jqXHR, exception) {
// other stuff
}
}
// example controller to return Excel binary file
public FileResult DownloadFile(string fileName)
{
// other stuff
byte[] content = TempData["something"] as byte[];
return File(content, "application/vnd.ms-excel", fileName);
}
NB: The explanations above are mostly trivial, your current implementation may differ from given examples.
Similar issues:
Download Excel file via AJAX MVC
jQuery returning "parsererror" for ajax request
Why use Backend for something like creating an excel file & then downloading it.... its overkill & downloading part is difficut
use something light weight like Javascript on the client side... it will create excel from XML & will download it using the download
property...
var data_type = 'data:application/vnd.ms-excel';
var table_div = document.getElementById('table_wrapper');
var table_html = table_div.outerHTML.replace(/ /g, '%20');
Here is Solution
It could be a server side error in sending the file.
Have you tried changing the response Content Type to application/vnd.ms-excel ?
I show you a minimal working example
// Server side
public ActionResult GimmeFile()
{
var bytes = System.IO.File.ReadAllBytes(#"path_to_your_file.xlsx");
return File(bytes, "application/vnd.ms-excel", "Myfile.xls");
}
Client side with your Ajax call
$.ajax({
method: 'POST',
url: '/Home/GimmeFile',
success: function (data) {
alert("S: " + data)
},
error: function (jqXHR, ex) {
console.log(ex)
}
})
Anyway, I don't know what you need to do with the excel file after ajax call,
but if your need is to save it to local then you should use the HTML5 < a download> instead

Stop Execution of HTTPPOST ActionMethod in MVC

I have webApplication in MVC Framework..
i have situation where i have to provide user to export some data to csv file
for that i have written following code ..
[HttpPost]
public ActionResult ExportReportToFile(ReportCriteriaViewModels posdata, string name)
{
string strQuery = GetReportQuery(posdata, name);
IEnumerable<REP_MM_DEMOGRAPHIC_CC> lstDemographics = ReportDataAccess.GetReportData<REP_MM_DEMOGRAPHIC_CC>(strQuery);
if (lstDemographics.Count() > 0)
return new CsvActionResult<REP_MM_DEMOGRAPHIC_CC>(lstDemographics.ToList(), "LISDataExport.csv");
else
return view(posdata);
}
Above code works fine... if in listResult Count is Greater than zero then i returns File to download.. but if i dont get any records in lstDemographics then i returns view..
My problem is when i dont get any result in lstDemographics, i dont want to return view coz it refreshes whole view.. so is there any way where we can stop execution of Action Method and browser doesn't refresh the view and stay as it is..
Thanks..
You will have to make an AJAX call to stop page refresh.
To achieve file export, we actually broke the process in two AJAX calls. First call sends a request to server, server prepare a file and store it in temp table. Server return the file name to AJAX call if there is data. If no data or error, it return a JSON result to indicate a failure.
On success, view make another AJAX request to download the file passing file name.
Something like this:
[Audit(ActionName = "ExportDriverFile")]
public ActionResult ExportDriverFile(int searchId, string exportType, string exportFormat)
{
var user = GetUser();
var driverSearchCriteria = driverSearchCriteriaService.GetDriverSearchCriteria(searchId);
var fileName = exportType + "_" + driverSearchCriteria.SearchType + "_" + User.Identity.Name.Split('#')[0] + "." + exportFormat;
//TempData["ExportBytes_" + fileName] = null;
_searchService.DeleteTempStore(searchId);
var exportBytes = exportService.ExportDriverFileStream(driverSearchCriteria, searchId, exportType, exportFormat, user.DownloadCode, user.OrganizationId);
if (exportBytes != null)
{
var tempStore = new TempStore
{
SearchId = searchId,
FileName = fileName,
ExportFormat = exportFormat,
ExportType = exportType,
DataAsBlob = exportBytes
};
var obj = _searchService.AddTempStore(tempStore);
return Json(fileName);
}
else
{
return Json("failed");
}
}
[HttpGet]
public ActionResult DownloadStream(string fileName, int searchId)
{
var tempStore = _searchService.GetTempStore(searchId);
var bytes = tempStore.DataAsBlob;
if (bytes != null)
{
var stream = new MemoryStream(bytes);
// TempData["ExportBytes_" + fileName] = null;
_searchService.DeleteTempStore(searchId);
return File(stream, "application/vnd.ms-excel", fileName);
}
_logger.Log("Export/DownloadStream request failed", LogCategory.Error);
return Json("Failed");
}
At client side, we do something like:
function ExportData(exportType, exportFormat) {
var searchId = document.getElementById('hfSelectedDriverId').value;
var model = { searchId: searchId, exportType: exportType, exportFormat: exportFormat };
//$('div[class=ajax_overlay]').remove();
//alert("The file will be downloaded in few minutes..");
$.ajax({
url: '#Url.Action("ExportDriverFile", "Export")',
contentType: 'application/json; charset=utf-8',
type: 'POST',
dataType: 'html',
data: JSON.stringify(model)
})
.success(function (result) {
result = result.toString().replace(/"/gi, "");
if (result == "" || result == "failed")
{
alert("File download failed. Please try again!");
}
else
{
window.location = '/Export/DownloadStream?fileName=' + result+"&searchId="+searchId;
}
})
.error(function (xhr, status) {
//
//alert(status);
});
//$('div[class=ajax_overlay]').remove();
}
You should create javascript function with $.getJSON method.
On controller side you just have to check, if you get data from database then return file path else return message.
Your JS code should be something like this:
$.getJSON(url)
.done(function (data) {
if (data.filePath) // If get data, fill filePath
window.location = data.filePath;
else
alert(data.msg);
});
And from controller you can create a HTTPGET Action method that return JSON data like:
return Json(new { msg = "No data found" }, JsonRequestBehavior.AllowGet);
Based on condition you can simple change msg with filePath.

MVC Parameter Binding from JSON into multiple parameters

I'm receiving JSON data from this method:
function CallAPI(controllerName, functionName, sendData, callback) {
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function () {
if (ajax.readyState == 4) {
if (ajax.status == 200) {
var response = JSON.parse(ajax.responseText);
callback(response);
}
else {
callback();
}
}
}
ajax.open("POST", "http://" + window.location.host + "/API/" + controllerName + "/" + functionName, true);
ajax.setRequestHeader('Content-Type', 'application/json');
ajax.send(JSON.stringify(sendData));
}
function someRequest() {
var data = {};
data.userID = 1;
data.someValue = 20;
CallAPI("Home", "Add", data, function (response) {
if(response) {
alert(response.Message); //This property always exist
}
else {
alert("internet/connection error.");
}
});
}
And my Controller is:
[System.Web.Http.HttpPost]
public HttpResponseMessage Add(int userID, int someValue)
{
//Here!
return Request.CreateResponse(HttpStatusCode.OK, new GenericResponseAPI() { Status = false, Message = "Generic Message." });
}
I can create a model and MVC would bind it accordingly, but I have so many of these requests that I don't want to create various models (or a big one containing all of the properties), but instead have these simple functions with basic type parameters like this.
What do I need to do to be able to work with the controller function above, without changing the content-type of the incoming message?
Edit: I found a workaround with this:
[System.Web.Http.HttpPost]
public HttpResponseMessage Add(JObject jObject)
{
int userID = (int)jObject.getValue("userID").ToObject(typeof(Int32));
int someValue = (int)jObject.getValue("someValue").ToObject(typeof(Int32));
return Request.CreateResponse(HttpStatusCode.OK);
}
I imagine the reason is that your function signature is:
function CallAPI(controllerName, functionName, sendData, callback)
and you're calling
CallAPI("Home", "Add", function (response) { ... })
So you're never actually sending data in the format MVC is expecting.
I'd double check this by using something like Fiddler and by debugging your JavaScript code using your browsers dev-tools.

404 Error When Accessing URL through AJAX

Not sure why, everything seems formatted fine, but I get the HTTP 404 error when attempting to access a function in my controller. Here's the aspx:
function CheckIfPacked() {
if ($("#OrderNumber").val() != "") {
var url = "/Packing/PackageTracking/CheckIfPacked";
$.ajax({
url: url,
cache: false,
data: "orderNumber=" + $("#OrderNumber").val() + "&actionyes=GetSalesOrder()",
success: function (data) {
var domElement = $(data);
if (data != "") {
$('#MessageDiv').append(domElement);
}
});
}
}
And here's the controller:
public Result CheckIfPacked(string orderNumber) {
var mesEntity = new MESEntities();
var packh = from packhead in mesEntity.Packing_Transaction_Headers
where packhead.Order_No_ == orderNumber
select packhead.Completed_by_Packer;
if (packh.First() == 0)
{
return new Result { Success = true, Message = string.Format("You have not finished packing order {0}, are you sure you want to navigate away from this page?", orderNumber) };
}
else
{
return null;
}
}
I think I've just stared at this too long. Thanks.
your method should be static and you should use webmethod attribute for your function :
[WebMethod]
public static Result CheckIfPacked(string orderNumber) {
var mesEntity = new MESEntities();
var packh = from packhead in mesEntity.Packing_Transaction_Headers
where packhead.Order_No_ == orderNumber
select packhead.Completed_by_Packer;
if (packh.First() == 0)
{
return new Result { Success = true, Message = string.Format("You have not finished packing order {0}, are you sure you want to navigate away from this page?", orderNumber) };
}
else
{
return null;
}
}

Categories