Correct practices for where to put images and static files in an ASP.NET MVC application? - asp.net-mvc

There is a directory in the standard ASP.NET template "Content" where most people seem to be putting in images and css files etc.
For instance stackoverflow's logo:
(source: stackoverflow.com)
actually is refered to with a server path containing 'content' in the URL (just do View Source for any SO page and you'll see this). So they obviously are storing images in "content/images/...".
src="/Content/Img/stackoverflow-logo-250.png"
Edit: Sometime in the last 10 years they changed the path - but this is what it used to be.
I dont particularly like this. My HTML ends up with /content all over it, and its slightly harder to migrate existing pages that just have /image. Fortunately my stylesheet doesnt end up with content all over it, as long as I save it in content\site.css.
An alternative is to put an images directory in the root, but then you get images at the same level as Controllers and Views which is pretty horrible.
I'd wondered about trying to add a redirection rule like this :
routes.RedirectRoute(
"images rule",
"Images/{*}",
"Content/Images/{1}"); // this is NOT valid code - I made it up
But that code doesnt work because I just made it up. I could use a third party redirection/rewriting plug-in but I want to keep everything 'pure' within the MVC model.
What has anyone else found in this area? Or is everyone just happy with an extra ("/content".length) bytes in their source for every image they serve.

To be honest, I don't think its really something to worry about... "/Content" is going to make a pretty minimal contribution to your page size. If you still want to do it, here are some options:
Option 1, if you are running on your own server, is to check out the IIS URL Rewrite module: http://learn.iis.net/page.aspx/460/using-url-rewrite-module/
Option 2 is to use either RedirectResult, or ContentResult, to achieve the same effect in the MVC framework
First, map a "catchall" route under "Images/" to a controller action, like so
routes.MapRoute("ImageContent",
"Images/{*relativePath}",
new { controller = "Content", action = "Image" })
Make sure it is above your standard "{controller}/{action}/{id}" route. Then, in ContentController (or wherever you decide to put it):
public ActionResult Image() {
string imagePath = RouteData.Values["relativePath"]
// imagePath now contains the relative path to the image!
// i.e. http://mysite.com/Images/Foo/Bar/Baz.png => imagePath = "Foo/Bar/Baz.png"
// Either Redirect, or load the file from Content/Images/{imagePath} and transmit it
}
I did a quick test, and it seemed to work. Let me know in the comments if you have any problems!

It's usually better to put images under a different sub domain. The reason for this is browsers limit the number of connections per URL. So if you use http://static.mysiste.com now the browser can open more concurrent connections due to it being in a different URL.

Related

How to download image from url and display in view

I am trying to download an image and displaying it in a view in rails.
The reason why I want to download it is because the url contains some api-keys which I am not very fond of giving away.
The solution I have tried thus far is the following:
#Model.rb file
def getUrlMethod
someUrlToAPNGfile = "whatever.png"
file = Tempfile.new(['imageprependname', '.png'], :encoding => "ascii-8bit")
file.write(open(data).read)
return "#{Rails.application.config.action_mailer.default_url_options[:host]}#{file.path}"
end
#This seems to be downloading the image just fine. However the url that is returned does not point to a legal place
Under development I get this URL for the picture: localhost:3000/var/folders/18/94qgts592sq_yq45fnthpzxh0000gn/T/imageprependname20130827-97433-10esqxh.png
That image link does not point anywhere useful.
My theories to what might be wrong is:
The tempfile is deleted before the user can request it
The url points to the wrong place
The url is not a legal route in the routes file
A am currently not aware of any way to fix either of these. Any help?
By the way: I do not need to store the picture after I have displayed it, as it will be changing constantly from the source.
I can think of two options:
First, embed the image directly in the HTML documents, see
http://www.techerator.com/2011/12/how-to-embed-images-directly-into-your-html/
http://webcodertools.com/imagetobase64converter
Second, in the HTML documents, write the image tag as usual:
<img src="/remote_images/show/whatever.png" alt="whatever" />
Then you create a RemoteImages controller to process the requests for images. In the action show, the images will be downloaded and returned with send_data.
You don't have to manage temporary files with both of these options.
You can save the file anywhere in the public folder of the rails application. The right path would be something like this #{Rails.root}/public/myimages/<image_name>.png and then you can refer to it with a URL like this http://localhost:3000/myimages/<image_name>.png. Hope this will help.

how to remove sitecore folder name in the url?

I created a sitecore year/month/day folder structure in the content tree, when i view each article under the folder node, the url could be http://local/landing/year/month/day/article1.aspx, how could I make the url like this: http://local/landing/article1.aspx?
just remove the year/month/day structure in the url.
Is there some function in sitecore like remove or hide special templates in the frontend url ?
Any help , Thanks .
You can do it in 2 ways:
Use IIS 7 Url rewrite module to change the url. This way the url will be rewritten before it gets to sitecore and you don't need to change any code. You can find more info at the iis website
You can create a custom Item resolver and add it to the RequestBegin sitecore pipeline. Alex Shyba wrote about it here.
It sounds like you may have thousands of these items, but even so, you may want to use the built in functionality of Sitecore and consider creating aliases for each of these items. Programmatically creating an the alias on an ItemSaved event or ItemCreated is probably easiest.
As #marto and #seth have said, you can use URL rewriting or aliases to solve this.
There is, however, a drawback to doing this, irrespective of how you choose to do it.
If you have very many items (your structure makes it sound like you may do) then either method will require that the URL is unique. Removing the date structure from the URL means that all items in your landing section will require unique URLs (whether inherited from their item names or by some other means). This can impact on SEO for your site, as authors may have difficulty finding an unused name that is also human readable and good for SEO. It's unlikely you want to use ugly GUIDs in your URLs.
2 options
Change Bucket configuration and the set the required folder structure, bucket configuration can be found in Sitecore.Buckets.config file
Extend GetFromRouteValue Item Resolver and overwrite the ResolveItem() method to get the bucket item.
The default GetFromRouteValue class reference can be found in Sitecore.MVC.config file and replace this with your own customized implementation.
We have implemented with customized routing and getting the exact item if the route path matches.
Thanks,
Jisha

How do SO URLs self correct themselves if they are mistyped?

If an extra character (like a period, comma or a bracket or even alphabets) gets accidentally added to URL on the stackoverflow.com domain, a 404 error page is not thrown. Instead, URLs self correct themselves & the user is led to the relevant webpage.
For instance, the extra 4 letters I added to the end of a valid SO URL to demonstrate this would be automatically removed when you access the below URL -
https://stackoverflow.com/questions/194812/list-of-freely-available-programming-booksasdf
I guess this has something to do with ASP.NET MVC Routing. How is this feature implemented?
Well, this is quite simple to explain I guess, even without knowing the code behind it:
The text is just candy for search engines and people reading the URL:
This URL will work as well, with the complete text removed!
The only part really important is the question ID that's also embedded in the "path".
This is because EVERYTHING after http://stackoverflow.com/questions/194812 is ignored. It is just there to make the link, if posted somewhere, if more speaking.
Internally the URL is mapped to a handler, e.g., by a rewrite, that transforms into something like: http://stackoverflow.com/questions.php?id=194812 (just an example, don't know the correct internal URL)
This also makes the URL search engine friendly, besides being more readable to humans.

Uploading Images - Security

Setup:
I am writing the Admin utility for an eLearning Package. Using this utility, the Tutors can write their courses, add/upload images, etc.
My problem is regarding security vulnerabilities when uploading files, specifically, image files.
The following code is my controller code for the POST that uploads a new image file:
[HttpPost]
public virtual ActionResult StepImage(int CourseId, int StepOrder, HttpPostedFileBase file)
{
service.CourseId = CourseId;
service.StepOrder = StepOrder;
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var fileExtension = Path.GetExtension(fileName);
if ((fileExtension == ".jpg") || (fileExtension == ".gif") || (fileExtension == ".png"))
{
service.StoreImageFileName(fileName);
var path = Server.MapPath("~/[path to where images are uploaded]/" + service.CourseId + "/");
if(!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
file.SaveAs(path + fileName);
}
else
{
// Refactor notice : Validation for invalid file extension
}
}
else
{
// Refactor notice : Validation for no file chosen
}
return RedirectToAction(MVC.Admin.StepEditor.Actions.Edit(CourseId, StepOrder));
}
You can see from the above code that I check for the file extension and only allow .jpg, .gif and .png.
Questions
I tried storing the files under the App_Data folder, but this resulted in a 403 forbidden response when the Views try to display the images.
So I put them in ~/Images/...
Is there a security risk with this? Can someone upload an .exe file with a .jpg extension and get it to execute baddie code?
It has to be said that the risk is low, as only Tutors will have permissions to use the page that uploads the file, but you just need one disgruntled tutor... Or they give their login details to a student... or whatever.
Any other security risks in that code?
PS:
The basics are taken from Scott Hanselman's and Phil Haack's blog posts on the subject of uploading files using ASP.NET MVC 2 +:
Phil Haack post
Scott Hanselman post
You get a 403 response when you place the images in the App_Data folder because IIS prevents any browser from directly accessing files in App_Data.
Placing them in ~/Images/ works but depending on the security on that folder this could mean that anyone can list the contents of ~/Images/ and/or view the images. By default, IIS forbids listing the contents of any folder but viewing the images by anyone is allowed. So, if someones knows the filename of the images, they can view them.
A solution is to use URL based authentication. Only the tutors (or whoever needs to) will be able to see the images then.
If you want complete control over who sees what on what pages, you could still place the images in the App_Data folder and then stream them to who- or whatever needs them like this.
About uploading an .exe as a jpg, this is certainly possible. It will even work for any type of file.
However, you save the file to the disk and don't do anything yourself with it. That makes the risk very small that the .exe-as-jpg is executed on your server. Unless someones finds an exploit in .net code that forces .net to execute the jpg, this is very unlikely.
You also show the jpg to the user in your views. That carries once again the security risk that possibly the user's browser has an exploit that forces it to execute the .exe-as-jpg on the user's computer. However, I think that is still very unlikely to happen (but not impossible. It has been done before e.g. see this).
To prevent this, you would need code on your server side that checks every image to see if it really is an image. However, these (possibly non-existent) security risks are very small and even exploiting this risks would require someone who knows quite a lot of computers and programming. Personally, I wouldn't worry about this.
Absolute security is impossible (Well, short of never turning your computer on. If it doesn't do anything, nothing can go wrong after all). It all depends how much security you want and how much time & money you can invest.

CDN/sub-domain, resources, and versioning

I’m hoping to investigate/implement a CDN (initially just via a sub-domain, though moving over to CDN in time) and am having a mare finding resources that talk about handling of versions of files on that sub-domain.
Most places I’ve worked previously have implemented caching of resources (images, javascript, css, etc.) and when wanting to change an image, have gone through the painful process of just changing the filename of the image, and changing the reference to it in the source code (so that customers see the new, not the cached image).
What I want to achieve
what I'd like is:
resources.domain.com
with sub-folders such as:
scripts/
images/
css/
etc.
not a problem, and will help with the yslow/page speed scores (assuming cookieless domain etc.)
But versioning of assets is something I want to resolve.
E.g.
resources.domain.com/images/promo_banner1.jpg
I'd probably have to cache and expire perhaps every 10-15 days.
Assuming we have something key come in as a business request, and we need to change it, I want to be able to override that. From what I understand, I could append a querystring (?1.1) to it to force browsers to see it as a different resource.
I know I can do this in MVC (or indeed ASP.NET) by creating a 'CompanyResource' html helper that will lookup against perhaps a resource file or something similar to see if we have a new version, and if so, append the version number as a querystring element, but there has to be a better way?
So, what has the community come up with?
how best to deal with resources in a sub domain (assume I've read all of the yslow/google backup docs around this)
what have folks come up with to handle versioning of assets (to minimise overall code changes when something updates) - code based helper methods to deliver assets based upon some rules?
Hopefully I haven't waffled too much.
Thanks for any and all help :)
Cheers,
Terry
just noticed this one hadn't been answered - we resovled our problem with Html helpers under ASP.NET MVC using the following.
In web.config we stored the 'resources_url' (in this case resources.mycompany.co.uk), and called the following:
<%= Html.MyCompanyResourceScript("~/scripts/jquery-1.4.2.min.js") %>
Which translated to an Html helper:
public static string MycompanyResourceScript(this HtmlHelper helper, string url)
{
string _out = String.Format(#"<script type=""text/javascript"" src=""{0}""></script>", url.ToMyCompanyUrlAction(helper.ViewContext.HttpContext));
return _out;
}
public static string ToMyCompanyUrlAction(this string url, HttpContextBase context)
{
return String.Format("{0}://{1}{2}",
(context.Request.IsSecureConnection ? "https" : "http"),
WebConfigurationManager.AppSettings["resources_url"],
VirtualPathUtility.ToAbsolute(url, "/"));
}
We created helpers for MyCompanyResourceImage and MyCompanyResourceCss with suitable parameters for alt tags/media types etc. also.
This means we can (from the outset) host via our own resources domain, and if in future we choose to move that over to a CDN, then we can do so with minimal fuss.
Hope that helps someone.
Cheers,
Terry

Resources