I am using ASP.NET MVC (latest version).
Imagine having 2 pages:
Page-1: "Enter data" >> Page-2: "Thank you"
After submitting Page-1 you are being redirected to Page-2.
My goal: I want to make sure that you can't go back to Page-1 when you hit the browser's back button once you made it to Page-2. Instead I want you rather to stay on Page-2 (or being pushed forward to Page-2 every time you hit the back button).
I have tried all different kind of things. The following is just some simplified pseudo code ...
[NoBrowserCache]
public ActionResult Page1(int userId)
{
var user = GetUserFromDb(userId);
if (user.HasAlreadySubmittedPage1InThePast)
{
// forward to page 2
return RedirectToAction("Page2", routeValues: new { userId = userId });
}
var model = new Page1Model();
return View("Page1", model);
}
[NoBrowserCache]
[HttpPost]
public ActionResult Page1(Page1Model model)
{
var user = GetUserFromDb(model.UserId);
if (user.HasAlreadySubmittedPage1InThePast)
{
// forward to page 2
return RedirectToAction("Page2", routeValues: new { userId = model.UserId });
}
// Save posted data to the db
// ...
return RedirectToAction("Page2", routeValues: new { userId = model.UserId });
}
public class NoBrowserCache : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Haha ... tried everything I found on the web here:
// Make sure the requested page is not put in the browser cache.
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
filterContext.HttpContext.Response.Cache.AppendCacheExtension("no-cache");
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);
filterContext.HttpContext.Response.Expires = 0;
}
}
If only I could make sure that a request is being sent to the server every time I hit the back button. But right now, clicking the back button just pulls Page-1 from my browser's cache without sending a request to the server. So currently, I have no chance to redirect you forward to Page-2 by server means.
Any ideas or suggestions?
Thanks, guys!
Btw: There is no login/authentication involved. So, I can't use Session.Abandon() or stuff like this. And I would rather use some server based code than javascript if possible.
EDIT 2017-5-12
Following #grek40, I made sure that the anti-chaching statements end up in the browser. I therefor completely removed the [NoBrowserCache]-ActionFilterAttribute from my C# code above. Instead I added the following statements in the <head> section of my _Layout.cshtml:
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
I confirm that these three lines are being rendered to my browser (used my browser's developer tools to inspect). However, caching still works. I can still move backward without any server requests. Tested this with Google Chrome v62, Firefox Quantum v57 and Microsoft Edge v41 (all on Win10). #
EDIT 2017-6-12
Again following #grek40's suggestions: tried Expires: 0 as well as Expires: -1. No difference. I still didn't manage, to turn off my browser's cache.
Finally I found a solution. It's javascript based, but very simple.
I just had to add the following snippet to my Page-2 (the page I don't want users to leave anymore once they got there):
<script type="text/javascript">
$(document).ready(function () {
history.pushState({ page: 1 }, "title 1", "#nbb");
window.onhashchange = function (event) {
window.location.hash = "nbb";
};
});
I found this here: how to stop browser back button using javascript
Thanks to all your support guys. But "my own" solution was the only one that worked.
This can be done by using javascript. Use the following code
<script type = "text/javascript" >
function preventBack(){window.history.forward();}
setTimeout("preventBack()", 0);
window.onunload=function(){null};
</script>
or check the following link1 and link2.
This little piece of code might help you in solving your issue.
<script type="text/javascript">
/*To retain on the same view on Back Click*/
history.pushState(null, null, window.location.href);
window.addEventListener('popstate', function (event) {
history.pushState(null, null, window.location.href);
event.preventDefault();
});
</script>
You can add a line of javascript to every page for a client-side solution:
history.forward();
See docs on MDN. When there is a page to go forward to (which is when the used pressed the BACK button), this will force the user to that page. When the user already is at the most recent page, this does nothing (no error).
Response.Cache.SetCacheability(HttpCacheability.NoCache); // HTTP 1.1.
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.
Related
My problem is that the backend server (written in grails) is automatically converting my request URL to be a different URL. Specifically, it is changing it from /UXChallengeAwards/processSelectedNotifications to /UXChallengeAwards/index.
--
In a template gsp file, I have defined a button that makes a jQuery ajax call when clicked on:
<button class="blue-link"
onclick="jQuery.ajax({type:'POST',
data:jQuery(this).parents('.multiSelectForm').serialize(),
url: '/ici/UXChallengeAwards/processSelectedNotifications/${challenge.id}',
success:function(data,textStatus){},
error:function(xhr,textStatus,errorThrown){}
})" >
The method UXChallengeAwardsController.processSelectedNotifications exists. It performs some work and then redirects to another action in the controller. In fact, this used to work. But somehow in the process of adding a second button I made a change which seems to have broken things.
When the button is now clicked, the request URL gets switched to /ici/UXChallengeAwards/index and a 404 is returned because index does not exist as an action in this controller.
I've googled, and the most common answer for when this happens is that a controller must return some results for the view. But I've seen plenty of examples of redirects in controllers, and I do not see what I am doing wrong. (I did try variants of rendering results, but with no success.)
Here is what my controller action looks like:
def processSelectedNotifications = {
def challenge
def checkboxes = params.list('selectCheckbox');
for (checkbox in checkboxes) {
// the checkbox contains the id of a ChallangeAward that should be published
ChallengeAwards challengeAwards = ChallengeAwards.get(checkbox.toInteger())
if (challengeAwards) {
// grab a challenge for use in the redirect, they are all the same
challenge=challengeAwards.challenge
publish(challengeAwards)
}
}
if (challenge) {
redirect action: 'challengeAwardsRemote', id: challenge.id
return
}
// render a failure message if we got here
render messageNS(code:"UX.ChallengeAwards.Publish.failure")
}
I would really appreciate any insights into what might be wrong, or how to go about tackling this issue. I've checked my UrlMappings, and this is the rule that should handle this controller/method request:
"/$controller/$action?/$id?"{ constraints {} }
Thank you very much!
I'm going to go ahead and answer my own question, in case it is helpful for other newbies.
It turns out that I was not getting an automatic redirect. Rather, I had an error in the button setup code, so that grails was using its default link behavior. (Which is to go to the controller that matches the view, and if no action is specified, use the index method.)
The code above was originally created using a remoteSubmit tag, but I found that the generated code did not support handling multiple forms on a single page very well. So, I copied that generated code and then tweaked it to handle the multiple forms. However, I wanted the styling to match up with what was already in place on the page, so I switched it to be a button. That's when things went awry.
Eventually, I ended up specifying an onClick function for the button, and then writing the ajax submit code in javascript. Which turned out to be much simpler.
Here is what the button specification ended up looking like:
<button type="submit" id="notifications" class="blue-link" >
<i class="fa fa-envelope-o"></i>
<g:messageNS
code="UX.DiscussionBoard.ChallengeAward.Button.notify" />
</button>
And the associated JavaScript:
jQuery(document).ready(function() {
var clkBtn = "";
jQuery('button[type="submit"]').click(function(evt) {
clkBtn = evt.target.id;
});
jQuery('.multiSelectForm').submit(function() {
var url = '/ici/UXChallengeAwards/processSelectedNotifications';
if (clkBtn == 'deletes') {
url ='/ici/UXChallengeAwards/processSelectedDeletes';
}
var errorTarget = jQuery(this).parents().find('.recipientMessage').val();
var requestData = jQuery(this).parents('.multiSelectForm').serialize();
var options = {
data : requestData,
type : 'POST',
url : url,
target : '#awardsTab',
error : function(data) {
jQuery('#' + errorTarget).html(data.responseText).show();
},
success : function(data) {
console.log("in success");
}
};
jQuery(this).ajaxSubmit(options);
return false;
});
anyone using framework7 to create mobile website? I found it was great and tried to learn it by myself, now I meet this problem, after I create my App, I want to do something on the starter page initialization, here, my starter page is index.html, and I set data-page="index", now I write this below:
$$(document).on('pageInit', function (e) {
var page = e.detail.page;
// in my browser console, no "index page" logged
if (page.name === 'index') {
console.log("index page");
});
// but I changed to any other page other than index, it works
// my browser logged "another page"
if(page.name === 'login') {
console.log('another page');
}
});
Anyone can help? Thank you so much.
I have also encountered with the same problem before.
PageInit event doesn't work for initial page, only for pages that you navigate to, it will only work for index page if you navigate to some other page and then go back to index page.
So I see two options here:
Just not use pageInit event for index page - make its initialization just once (just make sure you put this javascript after all its html is ready, or e.g. use jquery's on document ready event)
Leave index page empty initially and load it dynamically via Framework7's mainView.loadContent method, then pageInit event would work for it (that was a good option for me as I had different index page each time, and I already loaded all other pages dynamically from underscore templates)
I am facing same issue and tried all solutions in various forums.. nothing actually worked. But after lot of RnD i stumbled upon following solution ...
var $$ = Dom7;
$$(document).on('page:init', function (e) {
if(e.detail.page.name === "index"){
//do whatever.. remember "page" is now e.detail.page..
$$(e.detail.page.container).find('#latest').html("my html here..");
}
});
var me = new Framework7({material: true});
var mainview = me.addView('.view-main', {});
.... and whatever else JS here..
this works perfectly..
surprisingly you can use "me" before initializing it..
for using for first page u better use document ready event. and for reloading page event you better use Reinit event.
if jquery has used.
$(document).on('ready', function (e) {
// ... mainView.activePage.name = "index"
});
$(document).on('pageReinit', function (e) {
//... this event occur on reloading anypage.
});
Is it possible to reload an image with an identical file name from a server using jQuery?
For example, I have an image on a page, however, the physical image can change based on user actions. Note, this does not mean the file name changes, but the actual file itself.
ie:
User views image on default page
User uploads new image
Default image on page does not change(I assume this is due to the file name being identical, the browser uses the cached version)
Regardless of how often the code below is called, the same issue persists.
$("#myimg").attr("src", "/myimg.jpg");
In the jQuery documentation, the "load" function would be perfect if it had a default method of firing the event as opposed to binding a callback function to a successful/complete load of an element.
Any assistance is greatly appreciated.
It sounds like it's your browser caching the image (which I now notice you wrote in your question). You can force the browser to reload the image by passing an extra variable like so:
d = new Date();
$("#myimg").attr("src", "/myimg.jpg?"+d.getTime());
It's probably not the best way, but I've solved this problem in the past by simply appending a timestamp to the image URL using JavaScript:
$("#myimg").attr("src", "/myimg.jpg?timestamp=" + new Date().getTime());
Next time it loads, the timestamp is set to the current time and the URL is different, so the browser does a GET for the image instead of using the cached version.
This could be one of the two problems you mention yourself.
The server is caching the image
The jQuery does not fire or at least doesn't update the attribute
To be honest, I think it's number two. Would be a lot easier if we could see some more jQuery. But for a start, try remove the attribute first, and then set it again. Just to see if that helps:
$("#myimg").removeAttr("src").attr("src", "/myimg.jpg");
Even if this works, post some code since this is not optimal, imo :-)
with one line with no worries about hardcoding the image src into the javascript (thanks to jeerose for the ideas:
$("#myimg").attr("src", $("#myimg").attr("src")+"?timestamp=" + new Date().getTime());
To bypass caching and avoid adding infinite timestamps to the image url, strip the previous timestamp before adding a new one, this is how I've done it.
//refresh the image every 60seconds
var xyro_refresh_timer = setInterval(xyro_refresh_function, 60000);
function xyro_refresh_function(){
//refreshes an image with a .xyro_refresh class regardless of caching
//get the src attribute
source = jQuery(".xyro_refresh").attr("src");
//remove previously added timestamps
source = source.split("?", 1);//turns "image.jpg?timestamp=1234" into "image.jpg" avoiding infinitely adding new timestamps
//prep new src attribute by adding a timestamp
new_source = source + "?timestamp=" + new Date().getTime();
//alert(new_source); //you may want to alert that during developement to see if you're getting what you wanted
//set the new src attribute
jQuery(".xyro_refresh").attr("src", new_source);
}
This works great! however if you reload the src multiple times, the timestamp gets concatenated to the url too. I've modified the accepted answer to deal with that.
$('#image_reload_button').on('click', function () {
var img = $('#your_image_selector');
var src = img.attr('src');
var i = src.indexOf('?dummy=');
src = i != -1 ? src.substring(0, i) : src;
var d = new Date();
img.attr('src', src + '?dummy=' + d.getTime());
});
Have you tried resetting the image containers html. Of course if it's the browser that is caching then this wouldn't help.
function imageUploadComplete () {
$("#image_container").html("<img src='" + newImageUrl + "'>");
}
Some times actually solution like -
$("#Image").attr("src", $('#srcVal').val()+"&"+Math.floor(Math.random()*1000));
also not refresh src properly, try out this, it worked for me ->
$("#Image").attr("src", "dummy.jpg");
$("#Image").attr("src", $('#srcVal').val()+"&"+Math.floor(Math.random()*1000));
Using "#" as a delimiter might be useful
My images are kept in a "hidden" folder above "www" so that only logged users are allowed access to them. For this reason I cannot use the ordinary <img src=/somefolder/1023.jpg> but I send requests to the server like <img src=?1023> and it responds by sending back the image kept under name '1023'.
The application is used for image cropping, so after an ajax request to crop the image, it is changed as content on the server but keeps its original name. In order to see the result of the cropping, after the ajax request has been completed, the first image is removed from the DOM and a new image is inserted with the same name <img src=?1023>.
To avoid cashing I add to the request the "time" tag prepended with "#" so it becomes like <img src=?1023#1467294764124>. The server automatically filters out the hash part of the request and responds correctly by sending back my image kept as '1023'. Thus I always get the last version of the image without much server-side decoding.
Based on #kasper Taeymans' answer.
If u simply need reload image (not replace it's src with smth new), try:
$(function() {
var img = $('#img');
var refreshImg = function(img) {
// the core of answer is 2 lines below
var dummy = '?dummy=';
img.attr('src', img.attr('src').split(dummy)[0] + dummy + (new Date()).getTime());
// remove call on production
updateImgVisualizer();
};
// for display current img url in input
// for sandbox only!
var updateImgVisualizer = function() {
$('#img-url').val(img.attr('src'));
};
// bind img reload on btn click
$('.img-reloader').click(function() {
refreshImg(img);
});
// remove call on production
updateImgVisualizer();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img id="img" src="http://dummyimage.com/628x150/">
<p>
<label>
Current url of img:
<input id="img-url" type="text" readonly style="width:500px">
</label>
</p>
<p>
<button class="img-reloader">Refresh</button>
</p>
I may have to reload the image source several times. I found a solution with Lodash that works well for me:
$("#myimg").attr('src', _.split($("#myimg").attr('src'), '?', 1)[0] + '?t=' + _.now());
An existing timestamp will be truncated and replaced with a new one.
If you need a refresh of the exact URL and your browser has the image cached, you can use AJAX and a request header to force your browser to download a new copy (even if it isn't stale yet). Here's how you'd do that:
var img = $("#myimg");
var url = img.attr("src");
$.ajax({
url: url,
headers: { "Cache-Control": "no-cache" }
}).done(function(){
// Refresh is complete, assign the image again
img.attr("src", url);
});
Nothing else worked for me because while appending a token to the query string would download the new image, it didn't invalidate the image in the cache at the old URL so future requests would continue to show the old image. The old URL is the only one sent to the browser, and the server was directing the client to cache the image for longer than it should.
If this still doesn't refresh the image for you, see if this answer helps. For more information, here is documentation on the Cache-Control request header.
In the html:
foreach (var item in images) {
<Img src="#Url.Content(item.ImageUrl+"?"+DateTime.Now)" >
}
I simply do this in html:
<script>
$(document).load(function () {
d = new Date();
$('#<%= imgpreview.ClientID %>').attr('src','');
});
</script>
And reload the image in code behind like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
image.Src = "/image.jpg"; //url caming from database
}
}
How to hide iframe url From HTML source code?
<iframe src="http://mysite.com" frameborder="0" scrolling="no" width="728" height="90"></iframe>
You can use javascript to load the source, and it will not be visible in iframe url in page source code.
For example with jQuery:
<script type="text/javascript">
$(document).ready(function(e) {
$('iframe').attr('src','http://www.flickr.com/');
});
</script>
<body>
<iframe src="" />
</body>
Example here.
You can combine it with $.post to get the value serverside:
$.post('get-iframe-src.php', function(data) {
$('iframe').attr('src',data);
});
You can even load iframe itself to some element like:
$.post('get-iframe.php', function(data) {
$('#element_id').html(data);
});
etc. solutions are many, this is just one of.
You can't. If the URL isn't in the HTML, how would the browser know where to get it?
One thing you could try is to obscure it to make it slightly harder for someone to find it. You could have the src attribute be blank and then when the document is ready fetch the URL value from the server in a separate AJAX request and update the iframe tag to include that value in the src.
This would be a fair amount of work, however, and wouldn't really accomplish anything. The only thing it would prevent is somebody finding it by viewing the page source. They can still look at the "current version" of the HTML in any web browser's debugging tools. (Right click on an element and inspect it, which is nearly ubiquitous at this point.) Or any other normal traffic-sniffing tools will see it plain as day.
Ultimately, if the web browser needs to know a piece of information, then that information needs to be visible on the client-side.
There's no way to fully block source viewing. But there are a couple ways to disable right-clicking:
1) Javascript:
<script language="JavaScript">
<!--
var message="Your message goes here.";
function click(e) {
if (document.all) {
if (event.button == 2) {
alert(message);
return false;
}
}
if (document.layers) {
if (e.which == 3) {
alert(message);
return false;
}
}
}
if (document.layers) {
document.captureEvents(Event.MOUSEDOWN);
}
document.onmousedown=click;
// -->
2) Add the following into your tag: oncontextmenu="return false"
reference https://forum.powweb.com/archive/index.php/t-36161.html
I decided for solution that does not use javascript, because most of the time it will be possible to read the "hidden" content.
Moreover, changing iframe SRC with javascript, will keep URL hidden when checking the source. However, inspecting the code will show the real URL.
My code is in PHP; however, I believe that the logic can be translated to other programming languages. This is how it works:
I kept the iframe tag as usual:
<iframe src="dash_url.php"></iframe>
The trick is inside the iframe_url.php, where I validate the referer. If it is valid, page is redirected to iframe URL. If it is not, than URL will be a message.
<?
$iframe_url = "https://example.com";
$Referer = #$_SERVER["HTTP_REFERER"];
$RefererHost = #explode(":", explode("/", explode("//", $Referer)[1])[0])[0];
if ($RefererHost == $_SERVER["SERVER_NAME"]) {
header("Location: " . $iframe_url);
} else {
echo "Invalid URL";
}
?>
If visitor inspects the page or checks the source, iframe tag will show SRC as dash_url.php.
I have written some code using jQuery to use Ajax to get data from another WebForm, and it works fine. I'm copying the code to another project, but it won't work properly. When a class member is clicked, it will give me the ProductID that I have concatenated onto the input ID, but it never alerts the data from the $.get. The test page (/Products/Ajax/Default.aspx) that I have set up simply returns the text "TESTING...". I installed Web Development Helper in IE, and it shows that the request is getting to the test page and that the status is 200 with my correct return text. However, jQuery refreshes my calling page before it will ever show me the data that I'm asking for. Below are the code snippets from my page. Please let me know if there are other code blocks that you need to see. Thank you!
<script type="text/javascript">
$(document).ready(function() {
$(".addtocart_a").click(function() {
var sProdIDFileID = $(this).attr("id");
var aProdIDFileID = sProdIDFileID.split("_");
var sProdID = aProdIDFileID[5];
// *** This alert shows fine -- ProdID: 7
alert("ProdID: " + sProdID);
$.get("/Products/Ajax/Default.aspx", { test: "yes" }, function(data) {
// *** This alert never gets displayed
alert("Data Loaded: " + data);
}, "text");
});
});
</script>
<input src="/images/add_to_cart.png" name="ctl00$ctl00$ContentPlaceHolder1$ContentPlaceHolder1$aAddToCart_7" type="image" id="ctl00_ctl00_ContentPlaceHolder1_ContentPlaceHolder1_aAddToCart_7" class="addtocart_a" />
The easiest way is to tell jQuery not to return anything.
$(".addtocart_a").click(function(e){
// REST OF FUNCTION
return false;
});
Good luck! If you need anything else let me know.