C# - Changing Window Address when making an AJAX Call - c#

I have a C# page that checks that a user is logged in when making ajax calls and regular calls. I run the following check after determining the user is not logged in:
base.OnActionExecuting(filterContext);
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
return Content("<script type='text/javascript'> window.location = '/login' </script>");
}
else
{
filterContext.Result = new RedirectResult("~/Login/?referURL=" + filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.Url.PathAndQuery));
}
The problem I am running into is in the 'if' part. I get an error saying that the name 'Content' does not exist. I need a way to redirect my window location to '/login'.

Instead of returning plain text return Json data and grab your redirect url from there and set redirect on Ajax success.
var data = new { IsSucess = true,
redirectUrl = Url.Action("Action", "Controller")}
filterContext.Result = new JsonResult() { data ,
JsonRequestBehavior = JsonRequestBehavior.AllowGet };
Then use JsonResult data like this in your Ajax call,
success: function (data) {
if (data.IsSucess)
window.location = data.redirectUrl;

Related

Console Log Returning HTML Page Text Instead of JSON

I am trying to return a model in JSON form from a request sent as the following:
$(document).ready(function() {
(function(){
console.log("ran");
$.ajax({
type: "GET",
url: "https://clas.uconn.edu/Employees/Edit/22",
success: function(data) {
console.log("Success: " + data);
empData = data;
}
});
})();
});
My Controller for this method is:
// GET: Employees/Edit/5
public ActionResult Edit(int? id)
{
var id = employee.id;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
if (employee == null)
{
return HttpNotFound();
}
return new JsonResult() { Data = employee, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
Console.WriteLine("error");
}
However I am getting an entire html page in the consol log even though none of these controller actions return a view. Any ideas?
Edit:
After adding the datatype, I am getting an error in the console log saying:
GET http://localhost:59909/Employees/EmployeeLookupDisplay
net::ERR_CONNECTION_REFUSED
It is returning the entire page because there is an error in your request somewhere.
Add a block error to your ajax call and console.log on the xhr. You will get much more information about the error like this.
What you can try is making the request on POST and checking the properties of the context on the C# code. Sometimes adding dataType and the encoding helps for the request.
Additionally check for the returning status of your request on your browser developer tools. In chrome it is the network tab that shows all requests and their status.
Try This, but according to your code its always returning HTTP not foud which is HTML page. if you have data to employee. i mean your controller action getting success without any error. then you can try tis.
$.ajax({
type: "GET",
url: "https://clas.uconn.edu/Employees/Edit/22",
dataType: "json",
success: function(data) {
console.log("Success: " + data);
empData = data;
}
Maybe instead of returning new JsonResult() { Data = employee, JsonRequestBehavior = JsonRequestBehavior.AllowGet };, just return Json(employee, JsonRequestBehavior.AllowGet);'
Another thing i see:
on the first line you are doing var id = employee.id; -> where employee comes from? maybe the error is there.

ASP MVC - Download a file and then navigate to a view [duplicate]

I've got a controller action that downloads a dynamically generated file:
public ActionResult DownloadFile()
{
var obj = new MyClass { MyString = "Hello", MyBool = true };
var ser = new XmlSerializer(typeof(MyClass));
var stream = new MemoryStream();
ser.Serialize(stream, obj);
stream.Position = 0;
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=myfile.xml");
Response.ContentType = "application/xml";
// Write all my data
stream.WriteTo(Response.OutputStream);
Response.End();
return Content("Downloaded");
}
Just for reference:
public class MyClass
{
public string MyString { get; set; }
public int MyInt { get; set; }
}
This is working, and the file (myfile.xml) is downloaded.
However, the message "Downloaded" is not sent to the browser.
Similarly, if I replace return Content("Downloaded");
for return Redirect("www.something.com");
then the browser is redirected before the file downloads.
As a bit of a pre-amble, the user journey is:
User fills out form on previous view
Form is submitted
XML is generated and downloaded
User is redirected / "Downloaded" view is shown (so hitting F5 won't re-post the form)
As Ross has said, you can only return one response to a HTTP request.
What i do in that case is:
Send the request to the server
The server generates the file and stores it in some server side data structure (Cache, Usersession, TempData)
The server returns a RedirectToAction() (POST, REDIRECT, GET pattern)
The redirected action returns a View with some javascript which
Triggers the download of the pregenerated file by setting window.location.href property to an special download action which sends the file back to the browser
Each HTTP request can only have one response - you're trying to sneak in two (the file, and a page).
Normally when you send a "Content-Disposition: attachment" HTTP header the browser will stay on the current page and pop a file save dialog (or automatically save the file in your downloads).
You're going to have to change your strategy if you want to prevent re-submission of the form. I'd suggest a bit of javascript to disable the form's submit button and show the "Completed" message in a div overlay?
Here is how I redirected after the file is downloaded.
The main logic is to wait the redirect until the file is downloaded.
To do that, a server side response is calculated and redirect is delayed using server side response time + some offset.
Server Side Controller Code:
[HttpPost]
public ActionResult GetTemplate()
{
return Json(new {Url = Url.Action("ReturnTemplate") });
}
[HttpGet]
public ActionResult ReturnTemplate()
{
FileResult fileResult = // your file path ;
return fileResult;
}
Client Side Code:
<div id="btnGen" align="right"><button class="main-button" id="generateTemplate" type="Submit"></div>
Javascript:
$("#generateTemplate").click(function () {
var startTime = (new Date()).getTime(), endTime;
$.ajax({
url: '#Url.Action("GetTemplate", "Controller")',
type: 'POST',
traditional: true,
dataType: "json",
contentType: "application/json",
cache: false,
data: JSON.stringify(),
success: function (result) {
endTime = (new Date()).getTime();
var serverResponseTime = endTime - startTime + 500;
setInterval(function () { Back() }, serverResponseTime);
window.location = result.Url;
}
});
});
function Back() {
window.location = '#Url.Action("Index","Controller")';
}

handling un-handled exception MVC4 C# knockout and ajax

I am using knockout and using my custom function given below to make ajax call to controller. if any unhandlled exception occurs it returns entire page as response which results on reponse displaying entire html of page please suggest proper way of handling this
function asyncComputed(evaluator, owner) {
var result = ko.observable(), currentDeferred;
result.inProgress = ko.observable(false); // Track whether we're waiting for a result
ko.computed(function () {
// Abort any in-flight evaluation to ensure we only notify with the latest value
if (currentDeferred) { currentDeferred.reject(); }
var evaluatorResult = evaluator.call(owner);
// Cope with both asynchronous and synchronous values
if (evaluatorResult && (typeof evaluatorResult.done == "function")) { // Async
result.inProgress(true);
currentDeferred = $.Deferred().done(function (data) {
result.inProgress(false);
if (data.hasOwnProperty("HasFailed") == true && data.ErrorCode == 1)//For Session Time Out
{
$("#timeoutmessage")
.html('<div class="modal-backdrop">' +
'<div style="color:white;" class="middle">' +
'<div class="row-fluid"><div>Your session has timed out, please Login here again.</div></div>' +
'</div>')
.show();
}
result(data);
});
evaluatorResult.done(currentDeferred.resolve);
} else // Sync
result(evaluatorResult);
});
return result;
}
and my calling function is
self.currentRevenue = asyncComputed(function () {
self.currentRevenueinProgress(true);
var duration = self.duration();
var location = self.location();
return $.ajax({
url: getCurrentTotalRevenue,
type: "GET",
data: { "durationType": duration, "storeId": location }
}).done(function (result) {
self.currentRevenueinProgress(false);
if (result.hasOwnProperty("HasFailed") == true) {
self.currentRevenueError(true);
}else{alert('success');}
});
i am using asycComputed function for making ajax calls if any of the ajax call thows exception all of the ajax call fails and result html of the page in response.
Can i use filters here Please suggest
You could write a custom action filter on the server which catches the exceptions and changes them into JSON responses.
public class MyErrorHandlerAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
filterContext.Result = new JsonResult
{
Data = new { success = false, error = filterContext.Exception.ToString() },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
Then you just have to add the custom attribute to your controller action.

Passing a parameter from JavaScript to a controller

I am trying to pass a string value to a create item dialog, and am not sure on how to do it.
This is the code in my view:
JavaScript:
function newRoute() {
$.ajax({
url: '#Url.Action("Create")',
success: function (data) {
if (data == "success") //successfully created the new route
window.location.href = '#Url.RouteUrl(ViewContext.RouteData.Values)'
else
$.facybox(data); // there are validation errors, show the dialog w/ the errors
}
});
}
View:
<td>#route</td>
<td>
Add
</td>
Controller:
public ActionResult Create(string routeName = "")
{
PopulateRouteInfoViewBag();
var newRoute = new RouteInformation();
newRoute.Name = routeName;
return View(newRoute);
}
I'm trying to take the value in #route and pass it over to the Create controller to have my dialog pop up with the passed in string value.
Use the ActionLink html helper method and pass the route variable like this.
#{
string route="somevaluehere";
}
#Html.ActionLink("Add","Create","YourControllerName",
new { routeName=route},new {#id="addLnk"})
Now handle the click event
$(function(){
$("#addLnk").click(function(e){
e.preventDefault(); //prevent normal link click behaviour
var _this=$(this);
//do your ajax call now
$.ajax({
url: _this.attr("href"),
success: function (data) {
if (data == "success") //successfully created the new route
window.location.href = 'someValidUrlHere'
else
$.facybox(data);
}
});
});
});
Also, You may consider building the path to the new page(action method) and return that as part of your JSON result and let the client read it from the JSON.
instead of appending the route variable value to the querystring, you may consider it as the part of message body.
There are two options. 1, use Url.Action("controllerName", "actionName", new {routeName = "your route name here"}) or 2, use the data property of the object passed into $.ajax.
For 2 your javascript would look something like
function newRoute() {
$.ajax({
url: '#Url.Action("Create")',
data: {
route: "your data here"
}
success: function (data) {
if (data == "success") //successfully created the new route
window.location.href = '#Url.RouteUrl(ViewContext.RouteData.Values)'
else
$.facybox(data); // there are validation errors, show the dialog w/ the errors
}
});
}

session timeout on ajax call

I know this is duplicate but I could not get reliable solution(for asp.net web).
I just want to redirect to the login page if session expires.
I have tried following:
1. using jquery status code
$.ajax({
type: "POST",
url: "stream.asmx/SomeMethod",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
//success msg
},
error: function (request, status, error) {
if (status = 403) {
location.href = 'login.aspx';
}
}
});
Problem: this returns same status code(403) for other errors too, which I only expect for session timeout.
2. Sending json message whether session expired
code behind:
if (!object.Equals(HttpContext.Current.Session["User"], null))
{
Id = int.Parse(HttpContext.Current.Session["User"].ToString());
}
else
{
result = from row in dtscrab.AsEnumerable()
select new
{
redirectUrl = "login.aspx",
isRedirect = true
};
}
on $.ajax success:
success: function (msg) {
if (msg.d[0].isRedirect) {
window.location.href = msg.d[0].redirectUrl;
}
else {
//load containt
}
}
Problem: It's somehow desn't invoke ajax success line if session expires(it does return correct json). And even this is not a proper way if I have many number of ajax request in the page(should be handled globally).
However, I saw this post which is really good soltion but it's for mvc using AuthorizeAttribute: handling-session-timeout-in-ajax-calls
So, Is there I can use same concept used in mvc using AuthorizeAttribute in asp.net web api? If not, how I can troubleshoot those issue which I'm facing (any of above two mentioned)?
A 403 status code is going to cause jQuery to call the failure method. Keep the same code behind from your second try, but move the redirect handler to the failure method instead of the success method. In the success method, treat it as you normally would.
Problem:
I had same problem in my Razor MVC Application throwing exceptions while ajax calls made when session timed out.
The way I have managed to get this issue sorted is by monitoring each ajax requests by using a simple light weight Action Method (RAZOR MVC) returning a bool variable whether the Request is Authenticated or not. Please find the code below..
Layout/Master Page / Script file:
<script>
var AuthenticationUrl = '/Home/GetRequestAuthentication';
var RedirectUrl = '/Account/Logon';
function SetAuthenticationURL(url) {
AuthenticationUrl = url;
}
function RedirectToLoginPage() {
window.location = RedirectUrl;
}
$(document).ajaxStart(function () {
$.ajax({
url: AuthenticationUrl,
type: "GET",
success: function (result) {
if (result == false) {
alert("Your Session has expired.Please wait while redirecting you to login page.");
setTimeout('RedirectToLoginPage()', 1000);
}
},
error: function (data) { debugger; }
});
})
Then in Home Controller/Server side you need a method to verify the request and return the boolean variable..
public ActionResult GetAuthentication ( )
{
return Json(Request.IsAuthenticated, JsonRequestBehavior.AllowGet);
}
This will validate each ajax request and if the session got expired for any ajax request, it will alert the user with a message and redirect the user to the login page.
I would also suggest not to use standard Alert to Alert. User some Tool tip kind of formatted div Alerts. Standard JS Alerts might force the user to click OK before redirection.
Hope it helps.. :)
Thanks,
Riyaz
Finally, I ended up following.
public class IsAuthorizedAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var sessions = filterContext.HttpContext.Session;
if (sessions["User"] != null)
{
return;
}
else
{
filterContext.Result = new JsonResult
{
Data = new
{
status = "401"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
//xhr status code 401 to redirect
filterContext.HttpContext.Response.StatusCode = 401;
return;
}
}
var session = filterContext.HttpContext.Session;
if (session["User"] != null)
return;
//Redirect to login page.
var redirectTarget = new RouteValueDictionary { { "action", "LogOn" }, { "controller", "Account" } };
filterContext.Result = new RedirectToRouteResult(redirectTarget);
}
}
Handling client side
<script type="text/javascript">
$(document).ajaxComplete(
function (event, xhr, settings) {
if (xhr.status == 401) {
window.location.href = "/Account/LogOn";
}
});
</script>
you can set session time out expire warning some thing like ....
<script type="text/javascript">
//get a hold of the timers
var iddleTimeoutWarning = null;
var iddleTimeout = null;
//this function will automatically be called by ASP.NET AJAX when page is loaded and partial postbacks complete
function pageLoad() {
//clear out any old timers from previous postbacks
if (iddleTimeoutWarning != null)
clearTimeout(iddleTimeoutWarning);
if (iddleTimeout != null)
clearTimeout(iddleTimeout);
//read time from web.config
var millisecTimeOutWarning = <%= int.Parse(System.Configuration.ConfigurationManager.AppSettings["SessionTimeoutWarning"]) * 60 * 1000 %>;
var millisecTimeOut = <%= int.Parse(System.Configuration.ConfigurationManager.AppSettings["SessionTimeout"]) * 60 * 1000 %>;
//set a timeout to display warning if user has been inactive
iddleTimeoutWarning = setTimeout("DisplayIddleWarning()", millisecTimeOutWarning);
iddleTimeout = setTimeout("TimeoutPage()", millisecTimeOut);
}
function DisplayIddleWarning() {
alert("Your session is about to expire due to inactivity.");
}
function TimeoutPage() {
//refresh page for this sample, we could redirect to another page that has code to clear out session variables
location.reload();
}
4xx are HTTP error status codes and would cause jquery to execute the onFailure callback.
Also, beware of using 3xx for redirects when you want to process the payload. Internet Explorer, in my experience, just does a redirect (without looking at the payload) when a 3xx status code is returned.
I'd say, throw a 403 and handle the situation. To the client 403 implies the resource access is forbidden. There can be multiple reasons, which is OK I guess.
For those using a ScriptManager, you can easily check for ajax request and then redirect with the following code:
private void AjaxRedirect(string url)
{
Response.StatusCode = 200;
Response.RedirectLocation = url;
Response.Write("<html></html>");
Response.End();
}
Then check for request type and redirect accordingly (using routes here):
if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
{
var redirectUrl = RouteTable.Routes.GetVirtualPath(null, "Default", null).VirtualPath;
AjaxRedirect(redirectUrl);
}
else
{
Response.RedirectToRoute("Default");
}
The "Default" route is a route defined in the routes collection:
routes.MapPageRouteWithName("Default", "", "~/default.aspx");
If you prefer, instead of using ScriptManager for ajax request check, you can use:
if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") {
code here...
}

Categories

Resources