I'm trying to create a system to serve images and their resized versions from GridFS using MVC3 and IIS URL Rewrite 2. After testing, I've realized that serving images directly from filesystem is 10x faster than serving them using GridFS file streams. Then I've decided to keep the originals in GridFS and create a copy of the original file and resized versions on servers local file system using a combination of Url Rewrite 2 and Asp.Net handlers.
Here are the rewrite rules I use for serving the original and the resized version:
<rule name="Serve Resized Image" stopProcessing="true">
<match url="images/([a-z]+)/[a-f0-9]+/[a-f0-9]+/[a-f0-9]+/([a-f0-9]+)-([a-f0-9]+)-([a-f0-9]+)-([0-9]+)\.(.+)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="/Handlers/ImageResizer.ashx?Uri={REQUEST_URI}&Type={R:1}&Id={R:2}&Width={R:3}&Height={R:4}&ResizeType={R:5}&Extension={R:6}" appendQueryString="false" logRewrittenUrl="true" />
</rule>
<rule name="Serve Original Image" stopProcessing="true">
<match url="images/([a-z]+)/[a-f0-9]+/[a-f0-9]+/[a-f0-9]+/([a-f0-9]+)\.(.+)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="/Handlers/Images.ashx?Uri={REQUEST_URI}&Type={R:1}&Id={R:2}&Extension={R:3}" appendQueryString="false" logRewrittenUrl="true" />
</rule>
As you can see, rewrite engine checks if the file exist on the file system, and if not. rewrites the url and sends the request to the handler. Handler serves the stream and writes the file to the file system. On the next request, file is served directly from file system. I've seperated the files to folders by splitting their 24 char IDs (MongoDB Object ID as string) to avoid hunderds of thousands images in same folder.
Here is a sample original image request:
http://localhost/images/test/50115c53/1f37e409/4c7ab27d/50115c531f37e4094c7ab27d.jpg
This and the resized versions works without any problems.
Since this URL is too long and has duplicates in it, I've decided to use rewrite engine again to shorten the url to generate folder names automatically. Here is the rule which I put to the top:
<rule name="Short Path for Images">
<match url="images/([a-z]+)/([a-f0-9]{8})([a-f0-9]{8})([a-f0-9]{8})(.+)" />
<action type="Rewrite" url="images/{R:1}/{R:2}/{R:3}/{R:4}/{R:2}{R:3}{R:4}{R:5}" appendQueryString="false" logRewrittenUrl="true"></action>
</rule>
When I request an image using this rule for example with the following URL:
http://localhost/images/test/50115c531f37e4094c7ab27d.jpg
it only serves the image if the image is already on filesystem, otherwise I get the following error:
HTTP Error 500.50 - URL Rewrite Module Error.
The page cannot be displayed because an internal server error has occurred.
I've checked IIS Log File Entry for the request. It doesn't show any details except:
2012-08-02 14:44:51 127.0.0.1 GET /images/test/50115c531f37e4094c7ab27d.jpg - 80 - 127.0.0.1 Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.1+(KHTML,+like+Gecko)+Chrome/21.0.1180.60+Safari/537.1 500 50 161 37
On the other hand, successfull requests log the rewritten URL's like:
GET /Handlers/ImageResizer.ashx Uri=/images/test/50115c53/1f37e409/4c7ab27d/50115c531f37e4094c7ab27d-1f4-1f4-2.jpg&Type=test&Id=50115c531f37e4094c7ab27d&Width=1f4&Height=1f4&ResizeType=2&Extension=jpg
Elmah and EventLog also doesn't show anything. Added a filesystem logger to the top of my controller method and it doesn't log these particular problematic requests.
Can anyone suggest a workaround to get it work?
Edit: After RuslanY's suggestion about Failed Request Tracing, I've managed to identify the error:
ModuleName: RewriteModule
Notification: 1
HttpStatus: 500
HttpReason: URL Rewrite Module Error.
HttpSubStatus: 50
ErrorCode: 2147942561
ConfigExceptionInfo:
Notification: BEGIN_REQUEST
ErrorCode: The specified path is invalid. (0x800700a1)
Entire Trace Result can be see here (IE only)
Unfortunately, this is still not taking me to the solution since the second rule (therefore shortening rule) is working when the file exist on the file system.
As an alternative approach to using UrlRewrite to do this checking, why not using Application Request Routing w/ disk based caching. Dynamic images will be generated, and the caching infrastructure of ARR will save generated images to disk. Less mess, and I've used ARR to great success in production scenarios. Disk cache persist between IIS restarts and can live as long as you say (default is to use cache information from the response, but you can override this to be longer).
Application Request Routing
Related
We're moving our physical file server to cloud storage, but due to custom domain name limitations, we can't keep using the current domain for the new cloud storage.
When a request is made to:
https://old-subdomain.old-website.com/old-directory/image.png
Is it possible to preserve the user-entered URL from above in the location bar, while serving content from the cloud storage location?
https://new-subdomain.new-website.com/new-directory/image.png
The closest I've been able to achieve is a redirect (cannot get a rewrite to work) using the IIS URL Rewrite module, but this doesn't preserve the original URL:
<rule name="FileStorageRedirect" stopProcessing="true">
<match url="^old-directory(.*)$" />
<conditions>
<add input="{HTTP_HOST}" pattern="^old-subdomain.old-website.com$" />
</conditions>
<action type="Redirect" url="https://new-subdomain.new-website.com/new-directory{R:1}" />
</rule>
If it's possible to do what I'm asking, the solution doesn't need to be limited to using the IIS URL Rewrite module. I'm open to all solutions.
I have ASP.NET MVC site on IIS 7.5/Windows Server 2008 R2. Site have a set of http bindings to different domains, such as domain1.test.dev.com. domain2.test.dev.com, 10 bindings in total. They all are doing well, except one. Every request for an image(jpg, gif, png) returns 404 for this single binding. So entire layout has been loaded, all others requests feel good, but all images are broken with error 404 Not Found. There are no https bindings, they all are using http, port 80 and IP Address is 'All Unassigned'.
Could anyone help please?
The answer is found. There was an additional web.config file in ~/Images directory. This config contained specific redirection rule for one of the domain, so for this domain I saw 404, and others worked great. An additional web.config appeared in ~/Images directory accidentally, because of missclicking
in URL Rewrite module on the IIS. Some time ago we have used such redirections for demo and removed them after that. The redirection rule in web.config was a kind of:
<system.webServer>
<rewrite>
<rules>
<rule name="ProductionRedirectDomain1" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{CACHE_URL}" pattern="*domain1.test.dev.com*" />
</conditions>
<action type="Redirect" url="{C:1}domain1.com{C:2}" />
</rule>
</rewrite>
</system.webServer>
The bug has disappeared after removing a redundant config. Thanks, everybody!
I am trying to forward a specific URL on my website to another website and I can't figure out how to set the proper values in the web.config file for my IIS server.
For example I would like to forward
www.example.com/ballot
to
ballot.example.com
So far I've tried the following but it doesn't have any affect to the url
<rewrite>
<rules>
<rule name="Imported Rule 1" stopProcessing="true">
<match url="^/ballot$" ignoreCase="false" />
<conditions>
<add input="{HTTP_HOST}" pattern="^example\.com$" />
</conditions>
<action type="Redirect" redirectType="Permanent" url="http://ballot.example.com/" />
</rule>
</rules>
</rewrite>
I'm hosting the website on a Rackspace Cloud site.
Okay, I figured this one out and I'm posting the solution here for anyone else that may come across a similar situation.
If you want to redirect a certain URL like http://www.example.com/ballot to http://ballot.example.com/
Then follow these steps
If one doesn't already exist create a directory called ballot directly under the web root of www.example.com.
Create a Web.config file and place it in the ballot directory with the following content.
Make sure you change the destination to your actual destination.
If you are hosting on a Rackspace cloudsite you must rebuild the application (not sure about other host). You can rebuild the application by deleting the Web.Config file (if one was present) and uploading a new one, or by clicking the rebuild application link within the General Settings tab of the cloudsite you are trying to redirect. More information about rebuild applications in Rackspace here: http://www.rackspace.com/knowledge_center/article/how-to-rebuild-an-aspnet-application-in-cloud-sites.
Also here is a link to the page where I ultimately found the solution to what I was trying to accomplish: https://www.stokia.com/support/misc/web-config-response-redirect.aspx
I have a site that is being served out of Sitefinity CMS, at http://www.example.com/, I also have sites on the same server that are being served out of the same root location that are accessed as follows: http://www.example.com/sub-site/. All of these sub-sites are in php and resolve to specific php files such as index.php. There are a lot of these sub-sites and we're looking to remove a number of them from the server. We usually send variables in the get request to these sub-sites as follows: http://www.example.com/sub-site/?address=12&ID=22
What we want to do is set up a rewrite rule that will redirect incoming requests to non-existent sub-sites to an error page written in PHP. This rewrite rule should keep the query string intact so that we can still use those variables once the error page is reached.
Here is the re-write rule that we have:
<rewrite>
<rules>
<rule name="RedirectFileNotFound" stopProcessing="true">
<match url=",*" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Redirect" url="errorpage.php" appendQueryString="true" />
</rule>
</rules>
</rewrite>
So this rewrite rule works terrific, in fact, it works too well. When I go to the Sitefinity site's home page all is well, but when I click on any of the links on that page, I'm redirected to the error page. I've determined that the problem has to do with dynamic server content as the Sitefinity site links look like this http://www.example.com/order/ or http://www.example.com/info/. These directories don't exist and thus, the rewrite rule catches them each time.
Is there a way that I can redirect the subsites without having the sitefinity site respond the same way?
I found this S.O. post that is addressing a similar problem, but I was unable to find much assistance there.
i am currently using IIS7's Url Re-write module, but the main loophole of using the IIS7's url re-write module, is that i have to write rule for all the page,which i want to use on the website, i want to use a comman rule and redirect it to particular page (say homepage) and using global.asax i can redirect it to desired page...
Is it possible with url re-write or is there any tool available i can use for this purpose, or a code sample that could help me doing this.
i dont want extension in the url.
i have pages like index.aspx, news.aspx, artists.aspx, lessons.aspx... i want the urls like index, news, lessons, artists, i created a rule in web.config like
< rewrite>
< rules>
< rule name="urlType1">
< match url="^(\w*)" />
< action type="Rewrite" url="default.aspx" appendQueryString="false" />
< /rule>
< /rules>
< /rewrite>
this will land any page to default.aspx, and then using rawUrl in the global.asax, i am checking for the page like if user has entered "news" then i rewrite to news.aspx
Hope this has helped.
You can do just as you're suggesting in your question--redirect all requests to a single URL:
And then in your Global.asax you could call Server.Transfer( "~/file1.aspx" ) to forward the request to a particular file.
Or, you could transfer directly from your URL Rewrite rule and skip further processing in your Global.asax file. For instance, this rule will read the incoming URL that has no file extention, and then forward the request on to a file that has a file extension:
<rule name="Append a file extension to all requests discard querystring" stopProcessing="true">
<match url="^(.*)\?" />
<action type="Rewrite" url="{R:1}.aspx" />
</rule>
Read more about URL Rewriting rules on Ruslan Yakushev's blog at http://ruslany.net/.
[EDIT] Okay, so for what you are asking...as far as I know, you do need to create a specific rewrite rule for each page. I was thinking more along the lines of MVC, where when you go to:
/news
it routes to
/default.aspx
which calls the NewsController.Index and displays the news page from default.aspx. However, to actually break everything out into individual pages and just trying to remove the extension...as far as I know, you have to create a new rule for each instance.
Redirect rules can be configured from the web.config files.
E.g. Here's what WordPress does in the web.config file that's included with WordPress:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="wordpress" patternSyntax="Wildcard">
<match url="*"/>
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
</conditions>
<action type="Rewrite" url="index.php"/>
</rule></rules>
</rewrite>
</system.webServer>
</configuration>
The routes ALL traffic to the index.php page. The index.php file then reads what the URL is and spits out data based on the URL. It doesn't redirect it to a different page after the redirect, it--rather--decides what content to display.
MVC works along the lines of, you see this url:
/news > will call > NewsController.Index();
/news/index > will call > NewsController.Index();
/news/view > will call > NewsController.View();
/news/read/id > will call > NewsController.Read(id);
These Controllers, then typically get data from the database and apply the data to a "View" (html page that sits somewhere on the server with variables to display the data the controller passes to it).