JavaScript not firing in C# MVC4 - c#

I have the following code in my view :
<script type="text/javascript">
function OnCancelClick(e)
{
var jobId = e;
var flag = confirm('You are about to cancel job : ' + jobId + '. Are you sure you want to cancel this job?');
if (flag) {
$.ajax({
url: '/job/CancelJob',
type: 'POST',
data: { jobId: jobId },
dataType: 'html',
success: function (result) { alert('Job ' + jobId + ' was cancelled.'); document.location = "#Url.Action("Index", "Job")"; },
error: function () { alert('Something went wrong. Check the log for more information.'); }
});
}
return false;
}
</script>
In my view I also have :
<input type="submit" id="cancelButton" value="Cancel" onclick="javascript: return OnCancelClick(#Model.Id);" />
In my controller I have :
[HttpPost]
public ActionResult CancelJob(int jobId)
{
try
{
logger.LogInfo(string.Format("<start> Cancel-button clicked for job : {0}", jobId), jobId);
JobCommandService.ChangeStatus(jobId, 6);
logger.LogInfo(string.Format("<end> Cancel-button clicked for job : {0}", jobId), jobId);
return RedirectToAction("Index", "Job");
}
catch (Exception ex)
{
logger.LogError(ex.Message, ex, jobId);
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return Json(new { Success = false, Message = ex.Message });
}
}
When I run this in my VS2012 it works just fine.
When I deploy it to the server, I'm getting the message that something went wrong.
In my logging there is no trace of the button being clicked.

As per your comment, when deployed your app is installed in accindigoapps.blabla.lok/jobmonitor.
However your script has the url hardcoded as url: '/job/CancelJob'. That will mean:
when you are debugging from VS your script will work because the request is being sent to a url like http://localhost:XXX/job/CancelJob
however in production, the request will be sent to http://accindigoapps.blabla.lok/job/CancelJob, missing the jobmonitor part.
You need a way to inform your JS code about the base url of your application:
You could generate the Url in a Razor view using Url.Action("CancelJob","job") and pass that Url into your javascript code.
Another option would be to use Url.Content("~/") in some javascript of your base layout. That helper Url.Content("~/") will return only your application folder, / in your dev environment and /jobmonitor/ when deployed. That way you will have your app root-relative url available to any script, so you can use it to build root-relative urls as you were doing in your script:
<script>
var myApp = {};
myApp.BaseUrl = '#Url.Content("~/")';
</script>
//Some other script like yours would be able to keep using root-relative urls as:
$.ajax({
url: myApp.BaseUrl + 'job/CancelJob',
...
If you prefer to generate full urls, you could follow a similar approach. Have a look at this question
Hope it helps!

Related

failed to load resource: the server response with a status 500 (internal server error)

i had build a web form with autocomplete method, the program is working fine in IDE. But after i publish the application to IIS, the method is not working. the console show this error `failed to load resouces: server response with a status 500(internal server error) Index2) . Im suspected the jquery code didn't recognize the controller name.
CSHTML
<script type="text/javascript">
$(document).ready(function () {
$("#CardName").autocomplete({
source: function (request, response) {
$.ajax({
url: "Index2",
type: "POST",
dataType: "json",
data: { Prefix: request.term },
success: function (data) {
response($.map(data, function (item) {
return { label: item.CardName, value: item.CardId };
}))
}
})
},
messages: {
noResults: "", results: ""
}
});
})
</script>
Controller
[HttpPost]
public JsonResult Index2(string Prefix)
{
List<CardHolderDetails> getCardList = new List<CardHolderDetails>();
getCardList = _service.getCardList();
List<CardHolderDetails> ObjList = new List<CardHolderDetails>();
foreach (var value in getCardList)
{
ObjList.Add(new CardHolderDetails { CardId = value.CardId, CardName = value.CardName });
}
//Searching records from list using LINQ query
var CardName= (from N in ObjList
where N.CardName.StartsWith(Prefix)
select new { N.CardName, N.CardId });
return Json(CardName, JsonRequestBehavior.AllowGet);
}
The following code it is working fine during the development, but once publish to IIS it seem the jquery url cannot be read. any replacement for URL to controller function name?
If your _service.getCardList() returns null then the foreach will throw an exception which you have not handled.
Maybe that is the reason you are getting (500) internal server error and not the jquery url, as 500 error code suggests that something went wrong in the server code.
As a suggestion, You should use implement try - catch and log the exceptions somewhere to be able to resolve such issues.

Finding it impossible to post simple ajax

I am realy struggling with this and would apprecate any advice.
I have this field
<input id="scanInput" type="text" placeholder="SCAN" class="form-control" />
For which I would like to make an ajax call when the field changes, so I tried
<script>
$("#scanInput").change(function () {
console.log('ye');
$.getJSON("?handler=GetPartDetails", function (data) {
//Do something with the data.
console.log('yay?');
}).fail(function (jqxhr, textStatus, error) {
var err = textStatus + ", " + error;
console.log("Request Failed: " + err);
});
});
</script>
Where my PageModel has this method
[HttpPost]
public IActionResult GetPartDetails()
{
return new JsonResult("hello");
}
and the url for the page in question is /packing/package/{id}
Now when I change the input value, I see ye on the console, and I can see that the network called http://localhost:7601/Packing/Package/40?handler=GetPartDetails (the correct URL I think?) with status code 200
But My breakpoint in GetPartDetails never hits, and I don't see yay? in the console.
I also see this message from the fail handler:
Request Failed: parsererror, SyntaxError: JSON.parse: unexpected character at line 3 column 1 of the JSON data
But I'm not even passing any JSON data... why must it do this
I also tried this way :
$.ajax({
type: "POST",
url: "?handler=GetPartDetails",
contentType : "application/json",
dataType: "json"
})
but I get
XML Parsing Error: no element found
Location: http://localhost:7601/Packing/Package/40?handler=GetPartDetails
Line Number 1, Column 1:
I also tried
$.ajax({
url: '/?handler=Filter',
data: {
data: "input"
},
error: function (ts) { alert(ts.responseText) }
})
.done(function (result) {
console.log('done')
}).fail(function (data) {
console.log('fail')
});
with Action
public JsonResult OnGetFilter(string data)
{
return new JsonResult("result");
}
but here I see the result text in the console but my breakpoint never hits the action and there are no network errors..............
What am I doing wrong?
Excuse me for posting this answer, I'd rather do this in the comment section, but I don't have the privilege yet.
Shouldn't your PageModel look like this ?
[HttpPost]
public IActionResult GetPartDetails() {
return new JsonResult {
Text = "text", Value = "value"
};
}
Somehow I found a setup that works but I have no idea why..
PageModel
[HttpGet]
public IActionResult OnGetPart(string input)
{
var bellNumber = input.Split('_')[1];
var partDetail = _context.Parts.FirstOrDefault(p => p.BellNumber == bellNumber);
return new JsonResult(partDetail);
}
Razor Page
$.ajax({
type: 'GET',
url: "/Packing/Package/" + #Model.Package.Id,
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
input: barcode,
handler: 'Part'
},
success: function (datas) {
console.log('success');
$('#partDescription').html(datas.description);
}
});
For this issue, it is related with the Razor page handler. For default handler routing.
Handler methods for HTTP verbs ("unnamed" handler methods) follow a
convention: On[Async] (appending Async is optional but
recommended for async methods).
For your original post, to make it work with steps below:
Change GetPartDetails to OnGetPartDetails which handler is PartDetails and http verb is Get.
Remove [HttpPost] which already define the Get verb by OnGet.
Client Request
#section Scripts{
<script type="text/javascript">
$(document).ready(function(){
$("#scanInput").change(function () {
console.log('ye');
$.getJSON("?handler=PartDetails", function (data) {
//Do something with the data.
console.log('yay?');
}).fail(function (jqxhr, textStatus, error) {
var err = textStatus + ", " + error;
console.log("Request Failed: " + err);
});
});
});
</script>
}
You also could custom above rule by follow the above link to replace the default page app model provider

Request URL Too Long

I getting this error "Request URL Too Long" when i tried to download the file. The url shows the data that i want to download but it doesn't download.
How can i solve Request URL Too Long error when downloading
my code
Javascript
<script>
function DownloadIndexController(possID) {
$.ajax({
url: '#Url.Action("DownloadIndex", "Poss")',
contentType: 'application/json; charset=utf-8',
datatype: 'json',
data: { possID: possID },
type: "GET",
success: function (returnValue) {
window.location = '/DownloadIndex/Poss' + returnValue;
}
})
}
</script>
Controller
[HttpGet]
public virtual ActionResult DownloadIndex(int possID)
{
try
{
possFilename = possFilename.Replace(",", ",");
string fullPath = Filelocation + possFilename;
return File(fullPath, System.Net.Mime.MediaTypeNames.Application.Octet, possFilename);
}
catch (Exception ex)
{
throw ex;
}
You cannot use ajax to download a file in that way.
What you need to do is to generate a normal download link for every item in your grid, like:
#Html.ActionLink("Download", "DownloadIndex", "Home", new { possID = "123" }, null)
Where Home is the name of your controller, and you have to dynamically add the possID for every item instead of the hard coded 123 in my example.
In your code, you download the file using ajax, and then you redirect to an url containing the full file just after '/DownloadIndex/Poss'
You probably just want to redirect to the file, without using Ajax at all :
<script>
function DownloadIndexController(possID) {
window.location = '/DownloadIndex/Poss?possID=' + possID;
}
</script>

jQuery PageMethods 401 Authentication failed with FriendlyUrls

I have FriendlyUrls nuget package added to WebForm application.
In RegisterRoutes I have:
var settings = new FriendlyUrlSettings();
//settings.AutoRedirectMode = RedirectMode.Off;
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings);
I created 2 pages WebForm1.aspx and WebForm2.aspx
On WebForm1.aspx I referenced jQuery v1.9.1 in the head simply added the following inside the default div tag in the body:
<div id="dvResult"></div>
<script type="text/javascript">
$(function() {
$.fpm("GetCategories", '', function (res) {
$("div#dvResult").html(res.d);
}, function (xhr, ajaxOptions, thrownError) {
$("div#dvResult").html("<b>" + thrownError + "</b><br/>Status: " + xhr.status + "<br/>" + xhr.responseText);
});
});
$.fpm = function fpm(methodName, arguments, onSuccess, onError) {
var proto = (("https:" == document.location.protocol) ? "https://" : "http://");
var hostname = window.location.hostname;
if (window.location.port != 80)
hostname = window.location.hostname + ":" + window.location.port;
var loc = proto + "" + hostname + "/WebForm2.aspx";
$.ajax({
type: "POST",
url: loc + "/" + methodName,
data: "{" + arguments + "}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: onSuccess,
error: onError
});
};
</script>
WebForm2.aspx is kept stock standard after adding the file to the project, except for 1 method added to the code behind:
[System.Web.Services.WebMethod(EnableSession = false)]
public static string GetCategories()
{
return "hi";
}
When I run the page WebForm1.aspx I get the following result:
{"Message":"Authentication failed.","StackTrace":null,"ExceptionType":"System.InvalidOperationException"}
When view the request in fiddler I can see the friendly url did not strip the .aspx extension (which is a good thing):
http://localhost:14918/WebForm2.aspx/GetCategories
However as shown above, the FriendlyUrlSettings has the AutoRedirectMode set to RedirectMode.Permanent and when you uncomment the line for RedirectMode.Off and comment the Permanent out, then you actually get the result "Hi" printed on the screen.
Anyone has any ideas what the cause could be or how to add an exclusion to the routes?
I have tried to following but it does not seem to affect in any way the 401 result I keep getting:
//routes.Add(new Route("*Remote.aspx*", new StopRoutingHandler()));
//routes.Ignore("{remote}", new { remote = #".*\Remote.aspx(/.)?" });
You just saved my day.Below is the c# version of the code.In case of the master pages just paste PageMethods.set_path("default.aspx") before closing Content tag
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings, new CustomFriendlyUrlResolver());
}
public class CustomFriendlyUrlResolver : WebFormsFriendlyUrlResolver
{
public override string ConvertToFriendlyUrl(string path)
{
if (HttpContext.Current.Request.PathInfo != "")
{
return path;
}
else
{
return base.ConvertToFriendlyUrl(path);
}
}
}
This is late but in case someone has same issue. Simple fix, set RedirectMode.Off instead of RedirectMode.Permanent. For the Ajax part do the following for the url key:
$.ajax({
type: "POST",
url:'<%=ResolveUrl("sample.aspx/methodname")%>'
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (msg) {
alert("Worked");
},
failure: function (msg) {
alert("Failed");
}
});
I had similar issue, the above solution worked for me. This is a quick fix but I wouldn't recommend it for production. This error occurs partly because of the FriendlyUrl vs non FriendlyUrl redirect method settings hence the server is receiving requests from an unauthenticated user. For production, make sure to put in place necessary security details and accept request from authenticated users otherwise the exposed methods from code behind can cause a huge security risk.
Faced with stripping vast amounts of PageMethods from a large established application, I found the following alternative solution to switching over to WebApi (turning AutoRedirectMode off still allows my file extensions to be displayed when requested directly and I really don't want that).
Instead use a custom FriendlyUrls.Resolver in your App_Start/RouteConfig file. The only change to existing pages was to add the following markup to each page using PageMethods:
<script>PageMethods.set_path("/Pages/Subjects.aspx")</script>
Here is the sample code in VB:
Imports Microsoft.AspNet.FriendlyUrls
Imports Microsoft.AspNet.FriendlyUrls.Resolvers
Public Module RouteConfig
Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.EnableFriendlyUrls(New FriendlyUrlSettings() With {.AutoRedirectMode = RedirectMode.Permanent}, New IFriendlyUrlResolver() {New CustomFriendlyUrlResolver()})
End Sub
End Module
Public Class CustomFriendlyUrlResolver
Inherits WebFormsFriendlyUrlResolver
Public Overrides Function ConvertToFriendlyUrl(path As String) As String
If HttpContext.Current.Request.PathInfo <> "" Then Return path Else Return MyBase.ConvertToFriendlyUrl(path)
End Function
End Class
Hope that helps someone!
Ended up creating WebApi project and after a few new problems arriving (CORS related), got it working and actually feel that it's probably a better solution than pagemethods.

Upload and Process CSV File in ASP.NET MVC 4 Architectural Considerations

I am working on an ASP.NET MVC 4 Application that imports and processes a CSV file. I am using a standard form and controller for the upload. Here is an overview of what I am doing currently:
Controller Logic
public ActionResult ImportRecords(HttpPostedFileBase importFile){
var fp = Path.Combine(HttpContext.Server.MapPath("~/ImportUploads"), Path.GetFileName(uploadFile.FileName));
uploadFile.SaveAs(fp);
var fileIn = new FileInfo(fp);
var reader = fileIn.OpenText();
var tfp = new TextFieldParser(reader) {TextFieldType = FieldType.Delimited, Delimiters = new[] {","}};
while(!tfp.EndOfData){
//Parse records into domain object and save to database
}
...
}
HTML
#using (Html.BeginForm("ImportRecords", "Import", FormMethod.Post, new { #id = "upldFrm", #enctype = "multipart/form-data" }))
{
<input id="uploadFile" name="uploadFile" type="file" />
<input id="subButton" type="submit" value="UploadFile" title="Upload File" />
}
The import file can contain a large number of records (average 40K+) and can take quite some time to complete. I'd rather not have a user sitting at the import screen for 5+ minutes for each file processed. I have considered adding a console application to watch the uploads folder for new files, and process when something new is added, but would like to see what input I receive from the community before starting my journey down this path.
Is there a more efficient way to handle this operation?
Is there a way to perform this action, allowing the user to continue about his/her merry way, and then notify the user when processing is done?
The solution to the issue I was having is a bit complex, but works similar to the IFrame fix. The result is a pop-up window that handles the processing, allowing the user to continue navigation throughout the site.
The file is submitted to the server (UploadCSV controller), a Success page is returned with a bit of JavaScript to handle the initial kick-off of the processing. When the user clicks "Begin Processing", a new window is opened (ImportProcessing/Index) that loads the initial status (kicking off an interval loop that retrieves status updates) and then makes a call to the "StartProcessing" action, kicking off the processing process.
The "FileProcessor" class that I am using is housed in a static dictionairy variable within the ImportProcessing controller; allowing for status results based on the key. The FileProcessor is promptly removed after the operation is complete or an error is encountered.
Upload Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UploadCSV(HttpPostedFileBase uploadFile)
{
var filePath = string.Empty;
if (uploadFile.ContentLength <= 0)
{
return View();
}
filePath = Path.Combine(Server.MapPath(this.UploadPath), "DeptartmentName",Path.GetFileName(uploadFile.FileName));
if (new FileInfo(filePath).Exists)
{
ViewBag.ErrorMessage =
"The file currently exists on the server. Please rename the file you are trying to upload, delete the file from the server," +
"or contact IT if you are unsure of what to do.";
return View();
}
else
{
uploadFile.SaveAs(filePath);
return RedirectToAction("UploadSuccess", new {fileName = uploadFile.FileName, processType = "sonar"});
}
}
[HttpGet]
public ActionResult UploadSuccess(string fileName, string processType)
{
ViewBag.FileName = fileName;
ViewBag.PType = processType;
return View();
}
Upload Success HTML:
#{
ViewBag.Title = "UploadSuccess";
}
<h2>File was uploaded successfully</h2>
<p>Your file was uploaded to the server and is now ready to be processed. To begin processing this file, click the "Process File" button below.
</p>
<button id="beginProcess" >Process File</button>
<script type="text/javascript">
$(function () {
$("#beginProcess").click(BeginProcess);
function BeginProcess() {
window.open("/SomeController/ImportProcessing/Index?fileName=#ViewBag.FileName&type=#ViewBag.PType", "ProcessStatusWin", "width=400, height=250, status=0, toolbar=0, scrollbars=0, resizable=0");
window.location = "/Department/Import/Index";
}
});
</script>
Once this new window is opened up, the file processing begins. Updates are retrieved from a custom FileProcessing class.
ImportProcessing Controller:
public ActionResult Index(string fileName, string type)
{
ViewBag.File = fileName;
ViewBag.PType = type;
switch (type)
{
case "somematch":
if (!_fileProcessors.ContainsKey(fileName)) _fileProcessors.Add(fileName, new SonarCsvProcessor(Path.Combine(Server.MapPath(this.UploadPath), "DepartmentName", fileName), true));
break;
default:
break;
}
return PartialView();
}
ImportProcessing Index:
#{
ViewBag.Title = "File Processing Status";
}
#Scripts.Render("~/Scripts/jquery-1.8.2.js")
<div id="StatusWrapper">
<div id="statusWrap"></div>
</div>
<script type="text/javascript">
$(function () {
$.ajax({
url: "GetStatusPage",
data: { fileName: "#ViewBag.File" },
type: "GET",
success: StartStatusProcess,
error: function () {
$("#statusWrap").html("<h3>Unable to load status checker</h3>");
}
});
function StartStatusProcess(result) {
$("#statusWrap").html(result);
$.ajax({
url: "StartProcessing",
data: { fileName: "#ViewBag.File" },
type: "GET",
success: function (data) {
var messag = 'Processing complete!\n Added ' + data.CurrentRecord + ' of ' + data.TotalRecords + " records in " + data.ElapsedTime + " seconds";
$("#statusWrap #message").html(messag);
$("#statusWrap #progressBar").attr({ value: 100, max: 100 });
setTimeout(function () {
window.close();
}, 5000);
},
error: function (xhr, status) {
alert("Error processing file");
}
});
}
});
</script>
Finally the Status Checker html:
#{
ViewBag.Title = "GetStatusPage";
}
<h2>Current Processing Status</h2>
<h5>Processing: #ViewBag.File</h5>
<h5>Updated: <span id="processUpdated"></span></h5>
<span id="message"></span>
<br />
<progress id="progressBar"></progress>
<script type="text/javascript">
$(function () {
var checker = undefined;
GetStatus();
function GetStatus() {
if (checker == undefined) {
checker = setInterval(GetStatus, 3000);
}
$.ajax({
url: "GetStatus?fileName=#ViewBag.File",
type: "GET",
success: function (result) {
result = result || {
Available: false,
Status: {
TotalRecords: -1,
CurrentRecord: -1,
ElapsedTime: -1,
Message: "No status data returned"
}
};
if (result.Available == true) {
$("#progressBar").attr({ max: result.Status.TotalRecords, value: result.Status.CurrentRecord });
$("#processUpdated").text(result.Status.Updated);
$("#message").text(result.Status.Message);
} else {
clearInterval(checker);
}
},
error: function () {
$("#statusWrap").html("<h3>Unable to load status checker</h3>");
clearInterval(checker);
}
});
}
});
</script>
Just a thought but you could thread the processing of your CSV files and on completion of that task call another method that basically provides a modal dialog or some kind of javascript alert on the client side letting the user know that the processing has completed.
Task.Factory.StartNew(() => ProcessCsvFile(fp)).ContinueWith((x) => NotifyUser());
or something along those lines. I think that ultimately you are gonna wanna look at some kind of threading because it certainly does not make sense for a user to be stuck looking at a screen while some kind of server side processing takes place.

Categories

Resources