Handle concurrent requests on a action method .net MVC - c#

There is a ASP.NET MVC Action Method that redirects to a specific page, but also another method redirecting to another page. If both methods were hit at same time - how can I instruct C# to redirect only to first method URL. It shows that first method is hitting in debugger but second method hit after and redirects to last hit URL.
How can I redirect only to first hit method?
i have a ajax request that get hit every 5 secends.and hit signout method if not authenticated.
function Authenticate() {
ajax = $.ajax({
type: "POST",
url: "/EmployeePortal/Dashboard/Authenticate",
data: "q=123",
success: function (isAuthenticated) {
if (!isAuthenticated || count > 360) {
window.location.replace("/PortalAccount/SignOut");
} else {
count++;
}
}
});
}
also i have a link to sign out manually that hit the same method.but with a id to identify it separately.
public virtual ActionResult SignOut(string id)
{
object obj = new object();
lock (obj)
{
if (!id.IsEmptyOrNull())
{
ClearSession();
return Redirect(ConfigSettings.Current.CurrentConfigSetting.SignOutRedirectUrl);
}
}
Thread.Sleep(1000);
ClearSession();
ActionResult loginAction = MVC.PortalAccount.Login();
return RedirectToAction(loginAction);
}
Problem is sometimes when i hit the signout manually. The 5 seconds one will hit the same time and it will redirect to wrong page. how can i avoid that

Related

Ajax call gets cached, despite the appropriate parameter

I'm currently writing a MVC C# application. Everything works just fine. I have a bit of functionality, where I fill up a Bootstrap modal box using an Ajax call, but the new page gets cached, despite my efforts to prevent that.
On my main page I have the following actionhandler to fill up the modal box:
function setExtraPermsOrAtts(appID){
$.ajax({
cache:false,
url: "/Group/_modifyAppPermissionsOfGroup?appID=" + appID
}).done(function (result) {
$("#addApplicationBody").html(result);
$('#modal-add-application').modal('show');
});
}
This gets caught by the following method:
public ActionResult _modifyAppPermissionsOfGroup(int? appID = 0)
{
if (appID != 0)
{
ViewBag.selectedAppID = appID;
Session["selectedGroupAppID"] = appID;
ViewBag.modifyPermsLater = true;
}
Group group = (Group)Session["currentGroup"];
return View(group);
}
Another thing that might be relevant is the point where it 'goes wrong'. The resulting View in the Modalbox, has a few radio buttons, depending on the content of the database. There I do a razor statement to get the DB value:
bool valueOfRadButtons = BusinessLogic.Domain.GroupIS.getExistingGroupPermission(
Model.LoginGroupID, myItem.ApplicationPermissionID).LoginPermissionState;
Does anyone know where I'm going wrong? Is it the Ajax call? The ActionResult method in the Controller? Or the inline razor statement? I know the data gets saved properly, cause I see so in the DB
You can specify that the response shouldn't be cached like this:
Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
Response.Cache.SetValidUntilExpires(false);
Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
It can be more easy if you make your own attribute and decorate the action with it as shown here.

Forcing the user to go through another page before getting to the one in the URL

For example, if the user tries to access a Main action and enters mySite/Main in the address bar, I would like force that to route to mySite/beforeMain, which would then automatically redirect to Main after some processing. How can I do this with route mapping, or is this possible in other ways? I don't want to have query string parameters in the URL, or use TempData/etc. in case cookies are disabled.
Perhaps you could use Url Rewrite.
Alternatively you could use RedirectToAction:
public ActionResult Main()
{
if(Request.QueryString["redirected"].ToString() != "true")
{
return RedirectToAction("beforeMain", "mySite");
}
return View();
}
Then your beforeMain action could be:
public ActionResult BeforeMain()
{
//do some stuff
return RedirectToAction("Main", new { redirected = "true" });
}
Then when you hit main the second time it will skip the redirect and you wont end up in a loop.

Handing Events over session in ASP.NET MVC

Imaing we have a Controller with
1) An action which starts some long operation, writes something to a session and immediately renders some user-friendly message:
public ActionResult Search(string query)
{
_searchProvider.Ready += SearchResultReady;
_searchProvider.Request(query);
Session["query"] = query;
return Results();
}
private void SearchResultReady(IEnumerable<IObject> results)
{
Session["searchResult"] = results.ToList();
}
When the 'Search' is done, the result is gets saved Session.
The reason we do this is to display results when they will be ready(to be requested with ajax)
public ViewResult Results()
{
if (Session["searchResult"] == null)
return View("Wait");
var query = Session["query"] as string;
var list = Session["searchResult"] as IList<IObject>;
var model = new ResultModel(query, list);
return View("Results", model);
}
Now the problem is that on Ready event, the Session is null.
What is the proper way to to save the Contoller's state between requests
You're not going to be able to use sessions for this. Sessions are not transmitted via AJAX, so the API endpoint you're hitting never gets a session token to look up. In fact, if you're dealing with a true REST API there's no such thing as a session in the first place. HTTP is a stateless protocol.
Additionally, if you do any work inside the the controller action, the response will not be returned until the result is actually ready, negating the need to fetch it later with AJAX. Even if you implement async (which you aren't even doing here), that merely releases the thread back to the server pool so that it can field other requests until the action finishes; it does not return the response faster.
If you want to load the page first and then fetch data from a long running task, you should simply render the page and let the API endpoint do the work once the page fires off a request for it via AJAX. Implement async on that so you don't deadlock the thread, and present some loading animation to the user while they wait for the AJAX request to complete.
UPDATE
Controller Action
public ActionResult Search(string query)
{
return View();
// that's right: do nothing here, just return the view
}
JavaScript for View
var interval;
$(document).ready(function () {
var query = location.search.split('=')[1];
$.post('/do/some/work', { query: query }, function (data) {
// render data
clearInterval(interval);
});
function checkStatus() {
$.get('/check/on/status', function (data) {
// data would contain percentage value or something,
//use that to update progress bar
});
}
interval = setInterval(checkStatus, 1000);
});
That's all quick and dirty. You should find a more robust way to get the search query. Maybe even set it with a view model in your action and then return that into your view or something. Also, JavaScript should use proper namespacing so some other script doesn't run over your interval variable, etc.

How to use custom AuthorizeAttribute with AJAX

With help of fellow friends I managed to find a solution for my problem from this topic: Reusable way to allow an account to be used by a single person at a time
I have a SingleLogin class which inherits from AuthorizeAttribute and implements a custom AuthorizeCore method for the purpose of re-usability of my single-login code:
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
int userId = (int)WebSecurity.CurrentUserId;
using (var db = new UsersContext())
{
if ((httpContext.Session.SessionID != db.getSessionId(userId))
|| db.getSessionId(userId) == null)
{
WebSecurity.Logout();
isAuthorized = false;
httpContext.Response.Redirect("/Home/Index");
}
}
}
return isAuthorized;
}
Everything works fine except my JsonResult action:
[HttpPost]
public JsonResult MessageSave(string message)
{
bool messageSaved = false;
int userId = (int)WebSecurity.CurrentUserId;
message = HttpUtility.HtmlEncode(message);
// Model method - adding chat log - db
db.addChatLog(message, userId);
messageSaved = true;
return Json(new { messageSaved = messageSaved });
}
This method is triggered by Ajax POST call which you can see in code example below. Just basic POST.
EDIT 3
Please check these images: http://imgur.com/a/Cjael .. Hm I guess POST does trigger, but have no idea why does my alert not work when I try to test it before $.ajax ... As you can see in response I do get Home/Index page but I am not redirected to home/index immediately(text stays inside of textBox and page just waits..), I have to push enter one more time to be redirected.. Very strange.
EDIT2
Seems like I can't even access my jQuery even after I get logged out. I put some alerts inside of my .js file.
I have a separate .js file which is then put in my View as <script src="~/Scripts/custom/homeChat.js"></script> . I pass the Razor values from View into my JS file via HTML5 data-.
My textBox element #txtMsg, triggers my jQuery event, therefore when I am logged out it probably doesn't recognize my textBox element anymore, and doesn't trigger my jQuery event?
Element that triggers .js in view is:
#Html.TextBox("txtMsg")
JS:
$("#txtMsg").keypress(function (e) {
//when enter
if (e.which == 13) {
alert("ALERT DOESNT TRIGGER");
$.ajax({
type: "POST",
url: url,
data: JSON.stringify({ message: input }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
if (data.messageSaved) {
$("#txtMsg").val("");
}
else {
window.location.href = urlhome;
}
}
});
}
}
});
So if you can't even come into your event, how can you even know something went wrong? I have this ˙HandleUnauthorizedRequest but you are required that you can get into your jQuery event(in my case .keypress in the js code above) for this to work if I understand right.
EDIT: Additional explanation
So let me explain the scenario. If I login with my username "john" from Firefox and again with username "john" from chrome, next action I do in Firefox, it will log me out and redirect me to Home/Index, because someone else made a new login in Chrome.
That is ok. Since you are not logged in anymore, you get redirected normally to your Home/Index if your action is normal ActionResult and returns view.
The problem I have is, that I have some other functionality in the page, which uses Ajax POST, and since you are logged out you can't POST to that JsonResult action therefore you can't even receive callback of error, which redirects you to Home/Index. I put some alerts into my JS, but no alert triggers which is normal, because I am not allowed on that page anymore anyway. If I want that my onEnter textbox redirects me to Home/Index I have to press enter twice. Is that all that could be done?
I am interested in best approach for this AJAX problem. I don't know how I should call it, but as I read from my previous topic it is called "handling AJAX timeouts"?
Thank you very much.
You can handle errors on AJAX request this way
$.ajax({
type: "POST",
url: url,
data: JSON.stringify({ message: input }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
if (data.messageSaved) {
$("#txtMsg").val("");
}
else {
window.location.href = urlhome;
}
},
error: function(xhr, status, error) {
// TODO: may be check error or status or xhr.statusCode()
window.location.href = urlhome;
}
});
jQuery $.ajax() docs
If understand it correctly you want to handle the unauthorized ajax request.
In that case you can override the HandleUnauthorizedRequest method in your attribute:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
filterContext.Result = new JsonResult();
}
else
{
filterContext.Result = new HttpStatusCodeResult((int)HttpStatusCode.Forbidden);
}
}

Updating page content using JS during asynchronous operation

I am trying to solve an issue with webservice call in my MVC 4 project. What I am doing is that when user submits form on page, I call webservice and wait for result. Meanwhile server returns callbacks (it is returning strings containing finished steps of work on server side). What I would like to do is to display contents of "log" I am building from callback on the page and refresh it repeatedly to display the progress.
The issue I run into is, that either I will have asynchronous call of webservice in my controller in which case I am not waiting for result from webservice and that means that user will not stay on the page that should be displaying progress, or I will call it synchronously, in which case javascript on the page will not get response from controller until the final response arrives (so no updates are displayed).
My desired flow is:
User submits the form
Server is called in controller
Server sends callbacks, controller processess them by expanding "log" variable with whatever arrives
User still sees the same page, where "log" (contained in specific div) is being periodically refreshed by javascript while controller waits for final result from server
Server returns final result
Controller finishes its code and returns new view
This is my post method, which currently doesnt wait for the response and proceeds immediately further:
[HttpPost]
public async Task<ActionResult> SubmitDetails
(DocuLiveInstallationRequest submittedRequest, string command)
{
request = submittedRequest;
try
{
switch (command)
{
case "Test":
{
request.OnlyTest = true;
DocuLiveInstallationStatus installStatus
= await IsValidStatus();
if (installStatus == null)
{
ViewBag.Fail = Resources.AppStart.TestNoResult;
return View("SubmitDetails", request); ;
}
else
{
status = installStatus;
if (status.Result)
{
ViewBag.Success = Resources.AppStart.TestSucces;
ViewBag.Log = installLog;
}
TempData["installationStatus"] = installStatus;
return View("SubmitDetails", request);
}
}
case "Submit":
{
request.OnlyTest = false;
DocuLiveInstallationStatus installStatus = await Install();
if (installStatus == null)
{
ViewBag.Fail = Resources.AppStart.InstallationNoResult;
return View("SubmitDetails", request); ;
}
else
{
status = installStatus;
TempData["installationStatus"] = installStatus;
TempData["installLog"] = installLog;
return RedirectToAction("Login",controllerName:"Login");
}
}
}
ViewBag.TestFail = Resources.AppStart.SubmitFailure;
return View("SubmitDetails", request); ;
}
catch
{
return View();
}
}
this is javascript I prepared for the view:
<script type="text/javascript">
$(document).ready(function () {
//$("#submitDetails").click(function () {
var progress = 0;
//$("#submitDetails").attr('disabled', 'disabled');
var statusUpdate = setInterval(function () {
$.ajax({
type: 'GET',
url: "/AppStart/GetInstallProgress",
datatype: "application/html; charset=utf-8",
success: function (data) {
if (data && data != "") {
$("div.status-message").text(progress);
}
}
});
}, 2000);
//});
});
</script>
Currently I just display the log on the next page (at this stage of development server returns response very swiftly), but I need to call the server, display progress from callbacks while waiting for result and THEN navigate to another page (depending on the result). I feel like I am very close, but I can't get it working.
PS: I am open to other solutions than updating page content. I don't really mind how the goal will be accomplished, but updating the page is preferred by the customer.

Categories