AJAX Call to Controller from Partial View - asp.net

I have a Login page which is a partial view, and want to display database connection status when page is shown.
My idea is calling a database testing function in controller through AJAX, which returns the status string. But the controller function is not being fired, and the "success" function just get a HTML as response.
After hours of research but no success.
Could you please help. Thanks!
The controller returns login page:
public ActionResult Login()
{
return PartialView();
}
The AJAX call in Login page:
$(document).ready(function () {
setTimeout(testConn, 100);
});
function testConn() {
$('#msg').text('Connecting database...');
$.ajax({
type: 'GET',
url: '#Url.Action("TestDbConn", "Utils")',
contentType: 'application/json; charset=utf-8',
success: function (s) {
$('#msg').text(s); // s is HTML, not a string what I want
}
});
}
The TestDbConn function in UtilsController:
public JsonResult TestDbConn()
{
string result = "";
try
{
var user = db.UserGroup.FirstOrDefault();
if (user == null)
{
result = "No available data!";
}
else
{
result = "Database connected!";
}
}
catch (Exception ex)
{
result = $"Unable to connect:{ex.Message}";
}
return Json(result, JsonRequestBehavior.AllowGet);
}

Figured it out. It's nothing to do with the PartialView.
Just removes this from authorization section in the Web.Config.
<deny users="?" />
This line prevents users from accessing the Utils controller when not signed in, and [AllowAnonymous] tag does not help!

Related

Anti-Forgery Token was meant for a different claims-based user

I am working on a logout feature in the application we are using ASP.NET Identity login. I can login successfully but when I logout and then try to login again I get the following message:
The provided anti-forgery token was meant for a different claims-based user than the current user.
Here is my logout code:
public ActionResult Logout()
{
SignInManager.Logout();
return View("Index");
}
**SignInManager.cs**
public void Logout()
{
AuthenticationManager.SignOut();
}
After the user press the logout button he is taken to the login screen. The url still says "http://localhost:8544/Login/Logout". Since we are on the login screen maybe it should just say "http://localhost:8544/Login".
What worked for me was switching the order of the middlewares used. Add first app.UseAuthentication() and then the antiforgery stuff. This is how I did it:
app.UseAuthentication();
app.Use(next => ctx =>
{
var tokens = antiforgery.GetAndStoreTokens(ctx);
ctx.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
return next(ctx);
});
Doing it the other way around creates a token that is not meant for authenticated users.
You are returning a View, rather than calling RedirectToAction(). So what is happening is the view is running under the context of the logout request, where the user is still logged in. They won't be logged out until the request finishes.
So, try
public ActionResult Logout()
{
SignInManager.Logout();
return RedirectToAction("Index", "Home");
}
I found that users were experiencing this issue when they would submit the login page when already authenticated. I replicated this error by:
Opening two tabs when logged in,
Logging out from one,
Reloading both,
Logging in to one,
Trying to log in with the other. The error occurred before entry to the POST: /Account/Login action.
The majority of my users use the web app on a mobile device, so it made sense that they had bookmarked the login page and pulled it up and submitted when they had a tab opened in the background already logged in. I also surmised that sometimes they would have a dormant tab loaded with the login form and just pull that tab up and submit.
I realize that there are many ways to solve this issue. I solved this with two changes:
I added a check on User.Identity.IsAuthenticated to my "GET: /Account/Login" action:
if (User.Identity.IsAuthenticated)
{
try
{
return RedirectToLocal(returnUrl);
}
catch
{
return RedirectToAction("index", "Home");
}
}
In my controller I created a "check if logged in" action:
[AllowAnonymous]
public JsonResult CheckLogedIn()
{
try
{
return Json(new { logged_in = User.Identity.IsAuthenticated }, JsonRequestBehavior.AllowGet);
}
catch
{
return Json(new { logged_in = false }, JsonRequestBehavior.AllowGet);
}
}
And I called it repeatedly in the view to redirect all open login forms away from the login page when already logged in:
<script type="text/javascript">
setInterval(function () {
$.ajax({
url: '#Url.Action("CheckLogedIn", "Account")',
type: "GET",
}).done(function (data) {
if (data.logged_in) {
window.location = '/';
}
});
}, 5000);
</script>
This worked well for me. Hope it helps you.
Try this:
public ActionResult Logout()
{
AuthenticationManager.SignOut();
Session.Abandon();
return RedirectToAction("Index");
}
That will reload your login page which will provide you a new CSRF token.
I've been getting this same error on the login for a LONG time now, but haven't been able to work out why. Finally I found it, so I'm posting it here (although it's a slightly different cause) in case someone else has it.
This was my code:
//
// GET: /login
[OutputCache(NoStore = true, Location = System.Web.UI.OutputCacheLocation.None)]
public ActionResult Login()
{
return View();
}
//
// POST: /login
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
if (!ModelState.IsValid)
{
return View(model);
}
//etc...
This worked fine for 99.99% of the logins, but every now & then I got the above-mentioned error, although I couldn't reproduce it, until now.
The error only happens when someone clicks the login button twice in quick succession. However, if I remove the AuthenticationManager.SignOut line in the Login action, then it's fine. I'm not sure why I put that line in there, but it's causing the issue - and removing it fixes the problem.
I didn't have the AuthenticationManager.SignOut command as Sean mentioned in my Login method. I was able to reproduce by clicking on the login button more than once before hte next View loads. I disabled the Login button after the first click to prevent the error.
<button type="submit" onclick="this.disabled=true;this.form.submit();"/>
Try this:
public ActionResult Login(string modelState = null)
{
if (modelState != null)
ModelState.AddModelError("", modelState );
return View();
}
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model)
{
AuthenticationManager.SignOut();
return RedirectToAction("Login", "Controller", new { modelState = "MSG_USER_NOT_CONFIRMED" });
}

Validation in asp.net mvc

I have an action Create and method Save. The create action simply displays the view as shown below
public ActionResult Create()
{
return view();
}
In create view, i get few fields like Name, Address etc. Once the user enters the data and click on the save button, i call the Save method using ajax call.
In Save method, i am validating the data:
[HttpPost]
public bool Save(UserModel User)
{
if (ModelState.IsValid)
{
// save the data
return true;
}
else
{
return false;
}
}
the response of this method is used in ajax of Create View:
$(function () {
$("#btnSave").click(function () {
$.ajax({
type: "POST",
contentType: "application/json;charset=utf-8",
url: "/Contoller/Save",
data:
// parameters
async: false,
success: function (data_l) {
if (data_l == true)
{
alert("record has been saved in database");
window.location.href = "#Url.Action("Index","Controller")";
}
else{
alert ("Invalid Entry");
window.location.href = "#Url.Action("Create","Controller")";
}
},
error: function () {
console.log("there is some error");
}
});
});
What i want to achieve is based on the response from the Save method, i should perform two operation. If the data is properly validated, data is saved and index page is loaded back. If validation is failed, i want to display the create view with fields entered along with validation messages.
I am sure that data annotation attributes are properly used in the model. Can someone please help me in solving this.
I suggest you perform validation on server side i.e. in your controller. Perform input validation first, if all fine, save the data on persistent store. If the data is saved successfully load index view or list view (whichever view you want by either return View() or RedirectToResult()). If there are any problem add an error in the ModelState object and return the same view. At client the error will be displayed along with the data already entered by user.
For an example refer below code snippet (there might be other ways however this is what we use at the moment):
public ActionResult AddNewSearch(SearchViewModel searchModel)
{
if (User.Identity.IsAuthenticated)
{
if (ModelState.IsValid)
{
var organizationUser = this.GetUser();
if (organizationUser == null)
{
ModelState.AddModelError("RecordNotFound", string.Format("No organization user found!!!"));
return RedirectToAction("LogOff", "Account");
}
if (this.searchService.GetUserSearch(organizationUser.Id, searchModel.Name) != null)
{
ModelState.AddModelError("RecordAlreadyExists", string.Format("Search already exists!!!"));
return View(searchModel);
}
var userSearchDomainModel = mappingService.Map<SearchViewModel, Search>(searchModel);
searchService.AddUserSearch(organizationUser, userSearchDomainModel);
return RedirectToAction("Index", "Search");
}
}
return RedirectToAction("Index", "Search");
}

jQuery Ajax: Parse Error Getting Result to Alert Box

I have a very simple ajax call to my handler from jquery which is failing to retrive data and it gives me parsererror when i try to show the result in alert box.
ASP.NET Handler code:
public void ProcessRequest (HttpContext context) {
string json = new StreamReader(context.Request.InputStream).ReadToEnd();
context.Response.ContentType = "application/json";
context.Response.Write(json);
}
Jquery
$('.submit').on("click",function(e) {
e.preventDefault();
var data1 = { "hi": "hello" };
alert(data1.hi);
$.ajax({
url: "/charity-challenge/CompetitionHelper.ashx",
data: data1,
dataType: 'json',
type: 'POST',
contentType: 'application/json; charset=utf-8',
success: function (data) {
alert("response = " + data);
},
error: function (data, status) {
alert("FAILED:" + status);
}
});
});
Note: I can see the response coming fine in chrome while debugging. BUT somehow when i try to show it in alert box it gives me parsererror.
Also, I want to assign the json data in handler. i dont have any clue how to do that.
i have a sample calss like this in handler. how to loop through the json data and assign values to this variables so i can work on those.
public class userData
{
public string EmailAddress { get; set; }
public string EntryId { get; set; }
}
Found the work around to this.
i added a complete callback and now its showing the result in alertbox. i dont know why it is not working without it BUT if someone knows please post the answer.
here is the complete call back.
complete: function(xhr, status) {
if (status === 'error' || !xhr.responseText) {
alert("Error");
}
else {
var data = xhr.responseText;
alert(data);
//...
}
}
It has to do with your request payload being sent by the ajax call as hi=hello
As a test, try this (requires Newtonsoft.Json nuget):
public void ProcessRequest(HttpContext context)
{
//string json = new StreamReader(context.Request.InputStream).ReadToEnd();
context.Response.ContentType = "application/json";
context.Response.Write(JsonConvert.SerializeObject(new { hi = "hello" }));
}
So I guess you have to parse your input stream e generate the json correctly.
You could also fix this in the client side, by calling using JSON.stringify(data1) in your data parameter in the ajax call.

Trapping the error message inside jquery ajax - from asp.net mvc action

I was able to get the actual error message before when I was using jquery ajax+ asp.net web services. However, the same code inside jquery $ajax error no longer works.
Inside my .js I have
$.ajax({
contentType: 'application/json, charset=utf-8',
type: "POST",
url: "/Controller/DoSomething",
data: JSON.stringify({ varname: varvalue }),
cache: false,
dataType: "json",
success: function (wo) {
alert('yay!');
},
error: function (xhr) {
alert('error');
if (xhr.responseText) {
var err = JSON.parse(xhr.responseText);
if (err) {
alert(err.Message);
}
else {
alert("Unknown server error, please try again!");
}
}
}
});
Inside my Controller I have
public JsonResult DoSomething(string folderno)
{
CustomObject obj;
//get the obj value from repository here
throw new Exception("my test error message");
return Json(obj);
}
I looked at the Firebug and it appears that I am getting
"JSON.parse: unexpected character" error.
What I am trying to do here is to fake a situation when getting obj from repository throws an exception. Obviously, return Json(obj) never gets reached.
My question is, how do I deal with this situation and trap the error messages on the JS side? Do I need to do something in my controller?
In my earlier set up of Jquery+asp.net web services, I could throw an exception inside my web service method (as shown in my action now) and it would be trapped in my ajax error and the error message would be parsed out.
Now, it would appear that I need to catch the exception and pack in myself....question is how? And do I need to do this inside every action? This seems like a lot of work.
One thing I do is create a generic return object for AJAX calls.
Something like:
public class AJAXReturn
{
public string Message { get; set; }
public object Result { get; set; }
}
Then in your return functions wrap them in Exceptions (or create a generic exception handler) that will look something like:
public JsonResult DoSomething(string folderno)
{
CustomObject obj = new { FolderNo = folderno };
AJAXReturn result;
try
{
result.Message = "OK";
result.Result = obj;
}
catch (Exception ex)
{
result.Message = "ERROR";
result.Result = ex;
}
finally
{
return Json(result);
}
}
Edit: On the javascript side, just check your result for data.Message == 'OK'. If it isn't ok you can display either the specific exception info or anything you want.
Edit 2: Sorry I should've mentioned this will always return in the success callback so make sure you parse it there.

Partial ASP.NET MVC View submit

I'm new in ASP.NET MVC so the question could appear 'stupid', sorry.
I have a Partial View inside my Home view.
The Partial View submit a form calling an Action Method inside the HomeController.
It works fine with server validation, the problem is that after the post only the Partial View is rendered.
How can I render the entire Home view after post?
About the code:
Inside PartialView I have a form:
<% using (Html.BeginForm("Request", "Home")) { %>
Request is a ActionResult defined inside my HomeController.
[HttpPost]
public ActionResult Request(RequestModel model)
{
if (ModelState.IsValid)
{
// Saving data .....
}
else
{
// Show Server Validation Errors
return View();
}
}
At this time, after the post, the ascx shows the server validation erros but only the PartialView ascx code is rendered.
The Url looks like this after the post:
http://xxxxxxxxxxx/Home/Request
What I want is showing the entire Home view with the ascx inside showing server validation errors.
Try to do a partial submit using jQuery:
<script type="text/javascript">
$(document).ready(function () {
$("input[type=submit]").live("click", function () {
var f = $("input[type=submit]").parents("form");
var action = f.attr("action");
var serializedForm = f.serialize();
$.ajax({
type: 'POST',
url: action,
data: serializedForm,
success: function (data, textStatus, request) {
if (!data == "") {
// redisplay partial view
$("#formDiv").html(data);
}
else {
// do whatever on sucess
}
}
});
return false;
});
});
</script>
Assuming your view/ascx/HTML is something like this:
<div id="formDiv">
<% Html.RenderAction("Request"); %>
</div>
Change return type also:
[HttpPost]
public PartialViewResult Request(RequestModel model)
{
if (ModelState.IsValid)
{
// Saving data .....
}
else
{
// Show Server Validation Errors
return PartialView();
}
}
I was facing same issue in code, so I just made a small modification in my code and it worked.
Instead of returning the same view, I used
return Redirect(Request.Referrer)
Earlier:
return View();

Resources