I have a question related to cache invalidation techniques... I am trying to implement a mechanism for my website, that automatically invalidates browser cache (css, images...). I want to be able to programatically invalidate browser cache, whenever I update the website (change images or styles);
For example: for the current release, among others, the css and some of the images have changed. In this situation I want that after the update is finished, when a user performs a request to the website, his browser's cache to get automatically invalidated, thus forcing the re-download of the new images and styles. This should be done only for the client's first request... the following ones should be retrieved from cache (so setting the no-cache pragma is out of the question).
Here's what i've tried:
in the BeginRequest event handler, I added the following lines:
Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
Response.Cache.SetETag("\"e111293b17594f1487d136ea7e9314ac\"");
this sets the ETag in the response headers. I figured that if I change this ETag at each release, and set it at each request, the cache will be invalidated, but it seems that it is not. I used Live HTTP headers to see the results and the ETAG is correctly set up for the response, but the css and images are still taken from cache.
Any ideas of how I could accomplish this, or if it can be accomplished at all?
I have run into issues like this in the past. Unfortunately I couldn't find a really nice way to accomplish this so I had to come up with a workaround. I was only dealing with this issue for CSS files so I added an extra querystring parameter to every CSS reference, for example
<link rel="stylesheet" type="text/css"
href="default.css?buildnumber=<%= Buildnumber %>" />
The build number gets incremented with each release so the browser was forced to go look for this new file. Not an ideal solution, but it worked without a hitch.
For those who are seeking for a MVC5 solution:
Step1: Change the AssemblyInfo.cs file of the project to the following
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.*")]
Step 2: Create a class to get the current version info on your project:
public class Versioner
{
public static Version GetVersion()
{
Assembly thisAssem = typeof(Versionador).Assembly;
AssemblyName thisAssemName = thisAssem.GetName();
Version vrs = thisAssemName.Version;
return vrs;
}
//Not really necessary, just if you need to show this info
public static string GetDataBuild()
{
Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
DateTime buildDate = new DateTime(2000, 1, 1).AddDays(version.Build).AddSeconds(version.Revision * 2);
string displayableVersion = $"{version} ({buildDate})";
return displayableVersion;
}
}
Step 3: Call the class methods in the views which needs cache auto-refresh on new builds.
#{
Version _ver = <MyProject>.Classes.Extensions.Versioner.GetVersion();
}
//here, <MyProject>.Classes.Extensions is my path to the Versioner.cls class file, adjust it to your project's classes path
Step 4: Use the variable with the version string to append to your scripts or .css files
<script src="~/js/index.js?v=#_ver"></script>
<link href="/css/style.css?v=#_ver" rel="stylesheet" />
Related
I have a SSO login page built in ASP.NET where I authenticate users and then pass them onto another system. This other system, for unknown reasons, requires that Chrome be running with the switch --allow-running-insecure-content in order for some external peripheral devices to run with their website.
My customer is wanting to ensure the current Chrome browser instance is running with this flag/switch set, and if not to display an error/warning message.
Is there any way of detecting this within Chrome itself from ASP.NET? Or, perhaps a way to simulate loading a resource that requires the switch to be set, and then detecting if that resource loaded or not?
Yes, there is a way from the client-side to detect whether Chrome is running with --allow-running-insecure-content.
The trick is to add an insecure resource to the web page and then check to see if that resource was loaded using JavaScript. Using its normal security parameters, Chrome will not load insecure resources from a secure context.
Here's some sample code that does this:
HTML:
<!-- When Chrome is run in insecure mode, as desired, this message will be removed -->
<div class="not-insecure-warning">
Your browser is not configured properly. You must access this page with Chrome using <code>--allow-running-insecure-content</code>. Learn more.
</div>
JavaScript:
// Remove `.not-insecure-warning` elements
function removeNotInsecureWarning(){
var warnings = document.getElementsByClassName('not-insecure-warning');
for(var i = 0; i < warnings.length; i++){
var warning = warnings[i];
warning.parentNode.removeChild(warning);
}
}
var insecureScript = document.createElement('script');
// TODO: Replace URL to an empty `.js` file served over HTTP
insecureScript.src = 'http://code.jquery.com/jquery-3.2.1.js';
// If this insecure script loads, then the browser is running in insecure mode, remove the warning
insecureScript.addEventListener('load', removeNotInsecureWarning);
document.body.append(insecureScript);
See this example on JSFiddle. You should see the warning if you're running Chrome normally, but if you open a new window with insecure content allowed, the warning on this page will disappear.
If you need to pass this information to your server-side logic, you can either:
Use AJAX. Send an XMLHttpRequest if the insecure script fails to load using an event listener like insecureScript.addEventListener('error', isSecureSendPOSTRequest);
Use a hidden form input <input name="is_insecure" type="hidden" value="0"> that you can process immediately after the user logs-in. You can toggle the value of this input using the same kind of client-side logic shown above.
I don't know about a direct solution , but a possible solution is that you can open chrome using the shell script in the c# as follows.
class Module1 {
static void Main() {
System.Diagnostics.Process.Start("chrome.exe", "--allow-running-insecure-content");
}
}
As you want to add --allow-running-insecure-content for loading the browser with that flag
I have my views setup to pre-compile, and therefore, at runtime if I were to try and read the view file (e.g. "~\Views\User\Report.cshtml") I'd get the following dummy-text, as opposed to the contents of my view:
This is a marker file generated by the precompilation tool, and should not be deleted!
Problem is, I'd like to re-use the cshtml view, and rerender it another way at runtime, but I cannot due to the above restriction.
The scenario:
An admin can see a list of users in a /User/Report route. It outputs some HTML that has a list of all users, and their information in an HTML table. These admins frequently want to download this html file (styles and all) to email it as an attachment to someone else. They could, of course, go to File->Save in their browser, but I wanted to simplify that action by adding a link to the page "Download this report as HTML" that would simply return the same page's content, as a forced-downloaded HTML file (2012-07-11_UserReport.html).
So, what I tried to do was re-render the view by running the Report.cshtml file's contents through ASP.NET's File() method, like this:
var html = System.IO.File.ReadAllText(Server.MapPath(#"~\Views\User\Report.cshtml"));
var bytes = System.Text.Encoding.UTF8.GetBytes(html);
return File(bytes,"text/html",string.Format("{0}_UserReport.html",DateTime.Now.ToString("yyyy-MM-dd")));
But, like I mentioned earlier, the file comes back as the dummy-text, not the view, since I'm pre-compiling the views.
I understand that to get around the pre-compilition, I could simply copy the Report.cshtml file, and rename it to Report.uncompiled (adding it to the csproj as of course) and read the contents of it, that's an ok solution, but not good enough.
What I would really like to know is: Is there a way I can get at that pre-compiled content? I looked in the Assembly's embedded resources, and they are not there. Any ideas/suggestions?
Updated with current solution
So after searching around some more, and trying to use WebClient/WebRequest to just make a request to the route's URL and send the response back down to the user to download while at the same time trying to pass the user's .ASPXAUTH cookie (that made WebClient/WebRequest time out for some reason? I even tried to create a new ticket, same result) I ended up going with what I didn't want to do: duplicate the view file, and rename it so it's not precompiled.
The view file (Report.uncompiled) had to be modified a bit as it was, and then I ran it through RazorEngine's Razor.Parse method and got what I needed, but it just felt hackey. Would still like a way to access the view file (Report.cshtml) even after it's compiled.
var templateHtml = Razor.Parse(System.IO.File.ReadAllText(Server.MapPath(#"~\Views\User\Report.uncompiled")),model);
var bytes = System.Text.Encoding.UTF8.GetBytes(templateHtml);
return File(bytes, "text/html", string.Format("{0}_UserReport.html", DateTime.Now.ToString("yyyy-MM-dd")));
Would the WebClient class work?
using System.Net;
using (WebClient client = new WebClient ())
{
client.DownloadFile("http://yourwebsite.com/test.html", #"C:\directory.html");
// If you just want access to the html, see below
string html = client.DownloadString("http://yourwebsite.com/test.html");
}
Just have this fire whenever your user clicks a button and then it will save the current content of the page wherever? You could probably also have a directory selector and feed whatever they select into that second parameter.
It essentially does the same thing as the browser save as, if that's what you want.
Please read about my following situation. So we have a web application in .net. Because some customers wanted to pay for some custom layout, we first started to use themes. It was ok until there were more than 10. Because after that it was difficult to maintain and more of them wanted custom design, I had to design a solution so that the user could change only a few css attributes through a front-end page.
So when a user clicked a button a folder with its name and another css file was generated and he could change the attributes and save the new file. This file was dynamically loaded on the Page_Load on some basebage. This was ok, until more and more users payed for this and now we have tens of folders and expect hundreds. There is also the problem of rights, by default IIS doesn't have writing right to the application folder. Also the deployment process builds a new version by deleting the whole app folder and recreating one so we had to do a workaround and make a copy of this mega folder that holds the users css. And anyway, I don't like the idea of having hundreds of files not under source control.
And now, I have a new requierement. The users to be allowed to upload a custom image to replace one of our own. So i said it is time to move evrything to the database. For basic attributes I solved it.
I have a "mutant" aspx page:
<%# Page Language="C#" ContentType="text/css" %>
.labelText
{
color:<%= WebApplication1.UserProfile.LabelColor%>
}
So the UserProfile class gets it's data from a table.
And then in the page_load of the basepage
HtmlLink link = new HtmlLink();
link.Attributes.Add("type", "text/css");
link.Attributes.Add("rel", "stylesheet");
link.Attributes.Add("href", "~/Styles/Override.aspx");
this.Header.Controls.Add(link);
It works ok, the problem the images are used like this:
.divHeader
{
background: 15px 0 url("logo.gif")
}
That logo.gig will be uploaded by the user,but I would like to be saved in the database. However when reading it as a stream I can't do something like:
background: 15px 0 url(WebApplication1.UserProfile.Logo)
Is there any otehr way I can specify an object in a css class?If it isn't what is the best approach? I was thinking about keeping the image in the db and also as a file, something similar to what we have now.
I realize that if it were for an image control it would have been a lot easier, but this is the design now, and remaking it is a little bit troublesome
Thanks for reading this novel and for any oppinions on this.
I hope you're saving the mime-type value of every image that you save to database (mime type is reported by the request during upload). If you don't, you'll have to look up what a mime type would be based on image-file extension or its content format. You'll need this value when images are requested (from within CSS files).
Implement an HTTP handler (.ashx) specifically for these images (new file > generic handler > fill up ProcessRequest).
Then, replace url(logo.gif) in your css files with something like this:
url(/GetImage.ashx?user=1234&image=logo);
Obviously, you can have the filename and query to be whatever you like/need. Maybe you have user info in session, so you don't have to include user id there. Maybe you'll want to implement this handler only for logo images, and if you have user info in session, you can simply say (css):
url(GetLogo.ashx);
Then, within that ashx handler, write code to get the image from the database (or where ever it is), and stream it to the browser. Something like this:
byte[] imageData = ... // get this from db
MemoryStream ms = new MemoryStream(imageData); // System.IO namespace
Response.ContentType = "image/png"; // remember the mime-type?
ms.WriteTo(context.Response.OutputStream); // context is current HttpContext
Alternatively, you can inject the whole image into the CSS, by encoding it to base-64 format. Note that the size of css file will grow for the size of the image, and you're practically combining multiple downloads into 1, which may be a good or a bad thing.
Example below (google's logo); paste this in CSS (notice the size of that thing).
Besides CSS, the data in this example (data:image/png;base64,iVBORw0KGg ...) can be used as URL with IMG tags, or manipulated by JS.
.somediv
{
width:275px;
height:95px;
background-image: url(data:image/png;base64,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);
}
+1 if you like this :)
I'm building a little action to take an encrypted PDF file path, decrypt it, and deliver the resulting PDF to the browser.
My code works 100% of the time in Chrome and Firefox, but it works only 50% of the time in IE9.
When I follow the link in IE9, it looks like it opens the Adobe Reader plugin in the browser window, but no file is displayed until I hit refresh.
Here is my code:
[CheckSubscriber]
public ActionResult file(string path)
{
string mappedPath = Server.MapPath(
EncryptDecrypt.Decrypt(path,
EncString));
return base.File(mappedPath, "application/pdf");
}
How would I get this to work consistently in IE9?
I'm just thinking out loud here but maybe I am using the wrong mime-type?
You should be explicitly setting
Content-Disposition: inline; filename="foo.pdf"
The content-disposition is a crucial response header when returning a response from the server. All browsers will correctly detect the file 100% of the time if this is specified along with the MIME type.
You can use Fiddler to ensure that the response headers are in order.
Edit
You cannot use the "ActionResult" return type for your action to do this.
You need to use "FilePathResult" or "FileStreamResult" both of which can be found in the System.Web.MVC namespace.
Alternatively you can create a Custom Action Return Type and use that for this action.
The article I have provided gives step by step along with code as to how to go about doing this.
I would use Fiddler to see the difference between the request/response that the browsers send/receive and see if you can spot it from there.
Here's how I return an Excel file (pdf should be the same):
public FileResult DownloadErrors(string filename)
{
var file = System.IO.File.ReadAllText(filename);
return File(new System.Text.UTF8Encoding().GetBytes(file), "application/ms-excel", "Errors.csv");
}
Be sure to use FileResult instead of ActionResult.
I'm building a Flex widget for a private vBulletin site, and the Flex widget needs to access an XML file on the vBulletin server in order to display data.
For security reasons, the XML URL will need to have the value in the bbsessionhash cookie passed along in the URL request from Flex. The Flex widget will be embedded in the private area that the user has logged into, so the Flex request will be coming from the same website the cookie is from.
Is there any way to access the cookies directly within Flex? I would prefer not to use ExternalInterface to grab the cookie data from JavaScript, as it could get a little messy (the templates are developed by a completely different dev team).
I have never tried this, but this library might just do the trick.
As per the flash or flex cookies are concern developer can use shared object which is one kind of cookie used for flex application.
The sample code snippet is as followes
import flash.net.SharedObject;
// get/create the shared object with a unique name.
// If the shared object exists this grab it, if not
// then it will create a new one
var so: SharedObject = SharedObject.getLocal("UniqueName");
// the shared object has a propery named data, it's
// an object on which you can create, read, or modify
// properties (you can't set the data property itself!)
// you can check to see if it already has something set
// using hasOwnProperty, so we'll check if it has a var
// use it if it does, or set it to a default if it doesn't
if (so.data.hasOwnProperty("theProp"))
{
trace("already has data! It reads: " + so.data.theProp);
}
else
{
so.data.theProp = "default value";
so.flush(); // flush saves the data
trace("It didn't have a value, so we set it.");
}
Accessing Flex SharedObject is NOT the same as accessing the Browser cookies, to access the browser cookies, you may use the ExternalInterface class, please check the following reference to see samples:
http://livedocs.adobe.com/flex/3/html/help.html?content=passingarguments_4.html
A reference of how to use and control cookies using JavaScript can be found here:
http://www.quirksmode.org/js/cookies.html
I would use the following Flex code:
var myCookie:String = ExternalInterface.call("getCookie('cookieName')");
And in the HTML I would add the following Javascript:
function getCookie(c_name) {
var i,x,y,ARRcookies=document.cookie.split(";");
for (i=0;i<ARRcookies.length;i++) {
x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
x=x.replace(/^\s+|\s+$/g,"");
if (x==c_name) return unescape(y);
}
}
If you require more help you could also check the Flex documentation.