Here's the rundown. Users can view a razor page both anonymously and logged in. If they are logged in, they get certain features. In my controller, I have a boolean isAnonymous which I set to true or false depending on if there's a signed in user or not. I pass isAnonymous to my view model which gets sent to the razor page.
In the razor page, I have a javascript script tag which needs to retrieve that boolean value and, if isAnonymous is false (meaning someone is signed in), fire off one of two ajax calls to the server.
The first thing I do in my script tag is get the isAnonymous value and convert it to a JavaScript boolean with this:
var isAnonymous = #Json.Encode(Model.IsAnonymous);
after console logging, this appears to return correctly.
Then i put in my if statement. The summary here is if the user is not logged in, none of these functions nested inside the if statement should fire, because they take an ApplicationUser as part of the model. If there is no signed in user, Model.User is null and throws a Null Reference Exception. I thought putting my ajax calls inside the if statement would guard against the exception, but the the logic seems to be blowing right through the if (isAnonymous == false) and hitting those functions despite the logic. Any thoughts as to why this is happening? When isAnonymous is true, I can't have the functions fire.
if (isAnonymous == false) {
if ($('.bookmark-btn').hasClass('bookmark-story-btn')) {
addBookmark();
} else {
removeBookmark();
}
function addBookmark() {
//bookmark a story btn click event
$('.bookmark-story-btn').on('click', function () {
var storyid;
//the storyid should come as a string -
//try to parse it as an int for the controller
if (!isNaN($(this).attr('storyid'))) {
storyid = parseInt($(this).attr('storyid'))
//verify successful conversion from string to int before ajax call
if (typeof (storyid) == 'number') {
var userid = $(this).attr('userId')
var newBookmark = {
UserId: userid,
StoryId: storyid,
};
$.ajax({
url: "/api/bookmark/new",
method: "POST",
data: newBookmark,
success: function (data) {
//remove the save bookmark btn and dynamically add
//the remove bookmark btn so
//the page doesn't require a refresh
$('.bookmark-story-btn').remove();
$('.bookmark-btn-group').append("<button bookmarkId='"
+ data.Id
+ "' userId=#Model.User.Id storyId=#Model.StoryId"
+" class='btn remove-bookmark-btn bookmark-btn'>"
+"<i class='fas fa-2x fa-bookmark'></i></button>")
removeBookmark();
},
error: function (error) {
$('.page-alert').css('visibility', 'visible')
.html("Whoops. Something went wrong."
+" Adding the bookmark failed.")
//automatically close the alert-danger div
//after 2 seconds
setTimeout(function () {
$('.page-alert').css('visibility', 'hidden')
}, 3000);
}
});
}
}
});
}
function removeBookmark() {
//remove a bookmark click event
$('.remove-bookmark-btn').on('click', function () {
if (!isNaN($(this).attr('bookmarkid'))) {
bookmarkid = parseInt($(this).attr('bookmarkid'))
//verify successful conversion from string to int before ajax call
if (typeof (bookmarkid) == 'number') {
//call the ajax
$.ajax({
url: "/api/bookmark/" + bookmarkid,
method: "DELETE",
success: function (data) {
//show-hide the appropriate icons
$('.remove-bookmark-btn').remove();
$('.bookmark-btn-group').append("<button userId=#Model.User.Id"
+" storyId=#Model.StoryId class='btn bookmark-story-btn"
+" bookmark-btn'><i class='far fa-2x fa-bookmark'>"
+"</i></button>")
addBookmark();
},
error: function (error) {
$('.page-alert').css('visibility', 'visible')
.html("Whoops. Something went wrong here."
+" Removing the bookmark didn't work.")
//automatically close the alert-danger div
//after 2 seconds
setTimeout(function () {
$('.page-alert').css('visibility', 'hidden')
}, 3000);
}
})
}
}
})
}
}
You can use Request.IsAuthenticated in both the Razor view:
#if(Request.IsAuthenticated)
{
<script>
' your authenticated client side script here
</script>
}
And then check again server side when posting in your controller for example:
public ActionResult Index()
{
if(Request.IsAuthenticated)
{
//server logic here
}
}
Better still if you decorate the method with the AuthoriseAttribute the user will get an 403 Unauthorized.
You can then do something similar server side for the UserId:
[Authorize]
public ActionResult Index()
{
var userId = User.Identity.Name;
}
Then you don't even need to pass the UserId about. This is all based on using the common Identity practices:
https://learn.microsoft.com/en-us/aspnet/identity/overview/getting-started/introduction-to-aspnet-identity
Related
I have used the Asp.Net Identity framework in my app.There is a need, when the session expires give a prompt message, and then jump to the login page instead of directly jump to the login page.Prompt information using custom styles.
Because my app's left menus load the view with ajax,so I overried the AuthorizeAttribute.HandleUnauthorizedRequest methord to return a json.Now when users click left menus, it can work properly.But if users refresh the page by click F5,the page will still jump directly to the login page.
I have already overrided AuthorizeAttribute.HandleUnauthorizedRequest
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
var httpContext = filterContext.HttpContext;
string sintab = httpContext.Request["inTab"];
if (!string.IsNullOrEmpty(sintab) && bool.Parse(sintab))
{
var result = new JsonResult();
result.Data = new
{
Authorize = false,
Url = LOGIN_URL
};
result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
filterContext.Result =result;
return;
}
if (filterContext.Controller.GetType() != typeof(Controllers.HomeController) &&
!filterContext.ActionDescriptor.ActionName.Equals("Index", StringComparison.OrdinalIgnoreCase))
{
string returnUrl = "/" + filterContext.Controller.GetType().Name.Replace("Controller","") + "/Index" ;
returnUrl = httpContext.Server.UrlEncode(returnUrl);
httpContext.Response.Redirect("~/Account/Login?ReturnUrl="+returnUrl);
return;
}
base.HandleUnauthorizedRequest(filterContext);
}
The code of left menus' loadView js
$.get(url, null, function (html) {
html = html.replace(/#%/g, "\"").replace(/%#/g, "\"");
var json;
try {
json = eval("(" + html + ")");
} catch (e) {
}
if (json && !json.Authorize) {
// give an message
layer.alert("Session timeout, please re login.", function (index) {
window.location.href = json.Url + "?returnurl=" + encodeURI(hash);
});
}
else {
$("#content").empty().html(html);
_initModalButton();
$("#content").show();
}
}, 'html');
The page looks like this image
I want to know if there are some better ways to do this because there are a lot of other button need to check authorize status and show message before jump to the login page,and how to give the message when users refresh the page?
Thanks very much!
I think you're looking for are Global Ajax Events.
Please, check this, I think this make your job easier.
Here is the jquery code that is the problem. I wanted for the ajax to send json data to the server and then submit the form. If I don't have the when and done clause then it's possible for submission to be done before the ajax and will not be able to retrieve success or error in time.
function deleteImage(button)
{
//There is only one image that is a sibling of the delete button
var image = $(button).siblings(".MultiFile-image")[0];
var groupId = $(image).data("modelId");
var imgId = $(image).data("id");
var imgSrc = $(image).attr("src");
//Delete the image view after the removed button is clicked but the data to be sent to the server for deletion is already stored
$(button).parent(".MultiFile-label").remove();
var imageToDelete = {imgId:imgId, imgSrc:imgSrc, groupId:groupId};
var imageJSON = '{"imageToDelete":' + JSON.stringify(imageToDelete) + "}";
//This is needed to check whether ajax has been executed before submission
var sentImageData = false;
$("form").submit(function(e) {
//Stop submission, need to send data through ajax first, will submit after ajax is executed later.
if(!sentImageData)
{
e.preventDefault();
//Send the images for deletion only when the form has been submitted
//For some reason this code is never executed and go immediately to the end of this method
$.when(sendImageData(imageJSON)).done(function(jqXHR) {
if(jqXHR.readyState == 4 && jqXHR.status == 200)
{
sentImageData = true;
$("form").submit();
}
else
{
console.log(jqXHR);
sentImageData = false;
}
}); //For some reason the debugger skips to here and return is undefined
}
//If executed is true, send the form as normal
});
}
/**
* #var imageJSON the image json data that will be sent to the server to delete the image
* #returns {#exp;$#call;ajax} return XMLHttpRequest of the ajax
*/
function sendImageData(imageJSON)
{
return $.ajax({
type: 'POST',
data: imageJSON,
dataType: 'JSON',
url: "index.php?r=artworkGroup/deleteArtwork",
});
}
Thank you, I would much appreciate the help from the community on this problem :)
EDIT: Here is the action that handles this ajax code. an example of json is: "{"imageToDelete":{"imgId":2,"imgSrc":"upload_file/artwork/1-New_Artwork_Group/12861274.jpg","groupId":2}}"
public function actionDeleteArtwork() {
$noError = false;
if(isset($_POST["imageToDelete"]))
{
$imageArray = $_POST["imageToDelete"];
//Delete every image retrieved by post
foreach($imageArray as $image)
{
$transaction = Yii::app()->db->beginTransaction();
try{
$imageToDelete = json_decode($image);
$model = $this->loadModel($imageToDelete->groupId);
$artworkToDelete = $model->artworks->loadModel($imageToDelete->id);
if($imageToDelete->imgSrc == $artworkToDelete->imgSrc)
{
$artworkToDelete->delete();
if(file_exists($imageToDelete->imgSrc))
{
unlink($imgToDelete->imgSrc);
}
}
else
{
$hasError = true;
}
$transaction->commit();
}
catch(Exception $e)
{
$transaction->rollback();
$hasError = true;
}
//Delete the image files if there are no errors and that the file exists, otherwise just ignore
if(file_exists($imageToDelete->imgSrc) && $noError)
{
unlink($imageToDelete->imgSrc);
}
}
}
}
You have omitted url from your ajax request. that means it is going to hit your current page url. That may be triggering timeout.
and Timeout is kind of error in $.ajax. thats why your
sendImageData(imageJSON)
is returning you false. and by consequence of it your .done() is not getting executed.
I have the following action in ASP.NET MVC4
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
// ?? Need some code here
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
I have the following code that calls this:
$('#article').on('submit', '#loginForm, #registerForm', function (e) {
e.preventDefault();
var $form = $(this);
var href= $form.attr('data-href');
$form.validate();
if (!$form.valid()) {
return false;
}
var data = $form.serializeArray();
$.ajax(
{
data: data,
type: 'POST',
url: href
})
.done(submitDone)
.fail(submitFail);
function submitDone(content) {
$('#article').html(content)
}
function submitFail() {
alert("Failed");
}
return false;
});
If the registration works I would like to force the whole web page to refresh. Is there
a way that I can send back a message from the actionmethod to the javascript to
tell it that the registration works and the javascript should refresh the whole
web page?
I did try return RedirectToLocal("/"); but this definitely does not work. What
this does is to return a new page and then have it populated in the #article DIV.
There is nothing that will automatically refresh the browser from the server.
To refresh the browser from the server you'll need to send something from the server to the client indicating that you want to refresh the page. You'll need to write the javascript to look for the indication to refresh the browser.
Client Code
function submitDone(content) {
var json = $.parseJson(content);
if(json.isSuccess) {
//Do something here
}
$('#article').html(json.content)
}
Server code
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
// ?? Need some code here
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return Json(new {isSuccess = true, content = model});
}
I am unsure of what you are trying to accomplish by refreshing the page, if it's to clear out the form fields. The same could be achieved by using JavaScript. By using javascript instead of a page refresh you won't lose page state, such as error messages.
well i can think of a quick javascript trick to refresh a page on success like this
function submitDone(content) {
window.location.reload();
}
this will reload the page on the success.
I am using javascript to load up an Action and finding that when the action method is called one of the parameters "returnUrl" is always null. I have confirmed that returnUrl is populated in the javascript correctly using firebug, but somewhere between executing the .load function and the action method the value for returnUrl is lost and set to null. I have found that if I remove the "id" parameter and just have the "returnUrl" parameter that returnUrl has the correct value. I have spent many hours trying to figure out what is going on here and am completely stumped, I would apprectiate some help.
My Javascript:
<!-- Review Dialog Popup -->
<script type="text/javascript">
function showWriteReviewDialog(gameId, returnUrl) {
if( $("#Review").length == 0)
{
var url = "#Url.Action("WriteUserReview", "UGDBUser", new { id = "PLACEHOLDER", returnUrl = Request.Url.ToString() })";
// ajax load
$('#writereview').load(url.replace('PLACEHOLDER', gameId));
} else {
// clear summary & reviewtext fields
$('#summary,#reviewtext').val('');
//reopen the write review dialog which was previously rendered
$("#Review").dialog({
modal: true,
autoOpen: true,
resizeable: false
});
}
};
</script>
My Dumbed Down Action Method:
[Authorize]
public ActionResult WriteUserReview(Guid id, string returnUrl)
{
return Redirect(returnUrl);
}
There must be something wrong with the URL generated. Also make sure the id is a guid. Here is an example.
Option 1
function showWriteReviewDialog(gameId, returnUrl) {
var url = '#Url.Action("TestParams", "Home")?id=' + gameId + '&returnUrl=' + returnUrl;
$("#writereview").load(url);
//rest of your operations
};
Option 2
function showWriteReviewDialog(gameId, returnUrl) {
var url = '#Url.Action("TestParams", "Home", new { id = "guid_replace", returnUrl = "url_replace"})';
url = url.replace('guid_replace', gameId);
url = url.replace('url_replace', returnUrl);
$("#writereview").load(url);
//rest of your operations
};
Screen shot on hitting the action; it returns both the values (have a look at the watch window)
I have a form that I use for login purposes here:
<form id = "membershipInfo" method = "post" action = "Login.aspx">
with an submit button that I want to do a post method and a javascript onclick method like this:
<input type ="submit" id = "submitInfo" class = "MemberInfo" value = "Take Me There!" onclick = "authorize(accept, reject)"/>
The method in the onclick is an facebook authorize method that will pull information from the user (access token). I need this token to have the user proceed in my program. The issue is that when this button is clicked the post method will happen before the onclick is finished, which means that the access token will never be passed with the form.
To get the access token to the page load method I use a hidden input in the form:
<input type = "hidden" id = "hiddenAccessToken" name = "accessToken" value = "<%=accessToken %>" />
and in the Facebook method I get the access token like this:
function authorize(successCallback, failureCallback) {
FB.getLoginStatus(function (response) {
if (response.session) {
// logged in and connected user, carry on
session = response.session;
//Set access token
accessToken = response.session.access_token;
//Call success callback
successCallback();
} else {
// no user session available, Lets ask for perms
FB.ui(
{
method: 'permissions.request',
perms: permissionString
},
function (response) {
if (response && response.session != null) {
//Get session
session = response.session;
//Set access token
accessToken = response.session.access_token;
//Call success callback
successCallback();
} else {
//Call failure callback
failureCallback();
}
});
}
});
//Method hit on successCallback
function accept() {
//Add access token to hidden input
$('#hiddenAccessToken').val(accessToken);
}
I don't want to use a time out (and I don't think that will work anyways), but I need to slow the page load down until these js methods are complete. Any suggestions?
Why don't you do the form submit with js, like this:
<input type ="button" id = "submitInfo" class = "MemberInfo" value = "Take Me There!" onclick = "authorize(accept, reject)"/>
And then:
function accept() {
//Add access token to hidden input
$('#hiddenAccessToken').val(accessToken);
$('#membershipInfo').submit();
}