How do I route images through ASP.NET routing? - c#

I'd like to create a dynamic thumbnail resizer so that you can use the following URL to get a resized image:
http://server/images/image.jpg?width=320&height=240
I tried setting up a route like this:
routes.MapRoute(null,
"{filename}",
new { controller = "Image", action = "Resize" });
But if the file exists at the URL, ASP.NET will bypass the routing and return you just the file instead. How do I force ASP.NET to route the images instead of returning what's on disk?

Why not just use an action to do this? A controller's action can stream back an image. Otherwise, the typical way, say with ASPX, is that a handler or handler factory listens for the file extension and processes it accordingly. Or use URL rewriting to rewrite the URL in the request.

Thats how asp.net routing works, there is no away around that... you have to use Rewrite if you want to intercept requests for existing files.
Update
Seems like i was a bit too fast on the trigger there. There seems to be a property you can set which allows you to enforce a route even for existing files.
RouteCollection.RouteExistingFiles Property
http://msdn.microsoft.com/en-us/library/system.web.routing.routecollection.routeexistingfiles.aspx
Gets or sets a value that indicates whether ASP.NET routing should handle URLs that match an existing file. True if ASP.NET routing handles all requests, even those that match an existing file; otherwise, false. The default value is false.

You could also consider:
Writing a Module to handle these image routes before it hits routing (registered in Web.Config)
Write your own route handler specifically to handle these images.
Both would allow you to remove the need to write as a controller, I think this is cleaner.
Very basic example of your own route handler (from memory)...
Register as a normal route:
/* Register in routing */
routes.Add("MyImageHandler",
new Route("my-custom-url/{folder}/{filename}",
new ImageRouteHandler())
);
/* Your route handler */
public class ImageRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
string filename = requestContext.RouteData.Values["filename"] as string;
string folder = requestContext.RouteData.Values["folder"] as string;
string width = requestContext.HttpContext.Request.Params["w"] as string;
string height = requestContext.HttpContext.Request.Params["h"] as string;
// Look up the file and handle and return, etc...
}
}
Hope these help. Lots of ways to extend and achieve :)

The simplest way would be to route all images through the controller and store your images in a separate location
routes.MapRoute("Images",
"/images/{filename}",
new { controller = "Image", action = "Resize" });
/sitebase/images/image.jpg //public image location
/sitebase/content/images/image.jpg //real image location
Your controller would then see which image was being requested and load the appropriate file from the file system. This would allow you to do what you want without any special handling.

How about:
routes.MapRoute("Images",
"/images/{filename}.jpg",
new { controller = "Image", action = "Resize" });
That Should ensure that only URLs with .jpg as an extension get matched to that route and get routed appropriately.
Also remember you want to add your actions in order of most specific to least specific, with your default one being added last.
Of course, your action still needs to serve up the Image using a filecontentresult.

Related

ASP .NET MVC - files access

I'm trying to configure routing in my application in such way: paths to files should work for every directory except one special. And access to files in this direcotory should be processed by my controller action. If I write smth like this:
routes.Map("Specialfolder/{name}", "Controller", "Action");
it works only for unexisting files. Controller doesn't catches route for existing file
If I add this line:
routes.RouteExistingFiles = true;
Working with files in my folder is ok. But files in other directories aren't routed anymore. How to fix this?
Here is a sample ActionFilter sample code snippet that you can use
public class UriActionFilter : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
if (System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated)
{
// Sample: somehow identify the user, in case of a custom identity, replace the below line to get the user identifier
var user = System.Threading.Thread.CurrentPrincipal.Identity.Name;
// get the parameter that will let you know what is the image or path that is being requested now
var ctxParams = filterContext.ActionParameters;
// if the user does not have the permission to view, return 403
filterContext.RequestContext.HttpContext.Response.StatusCode = 403;
return;
}
base.OnActionExecuting(filterContext);
}
}
In this snippet, you have to replace with the corresponding service calls etc..
W.R.TO the OnAuthorization, I was mentioning that you can use so that any one that gets access to the image may hit the URI directly and that can be used filtered. We are using that way to restrict the URI access directly by a given user.
This will help you to override the MVC authorization of each request that the controller gets. This is a more of a restriction by URI alone. However, your case can be satisfied by the above action filter too as parsing of the uri parameters can be quite tricky at times.

Stackoverflow style URL (customising outgoing URL)

If I navigate to the following stackoverflow URL http://stackoverflow.com/questions/15532493 it is automatically appended with the title of the question like so:
http://stackoverflow.com/questions/15532493/mvc-custom-route-gives-404
That is, I can type the URL into my browser without the question title and it is appended automatically.
How would I go about achieving the same result in my application? (Note: I am aware that the question title doesn't affect the page that is rendered).
I have a controller called Users with an action method called Details. I have the following route defined:
routes.MapRoute("UserRoute",
"Users/{*domain}",
new { controller = "User", action = "Details" },
new { action = "^Details$" });
As this is an intranet application the user is authenticated against their Windows account. I want to append the domain and user name to the URL.
If I generate the URL in the view like so:
#Html.ActionLink("Model.UserName", "Details", "User", new { domain = Model.Identity.Replace("\\", "/") })
I get a URL that look like this:
Domain/Users/ACME/jsmith
However, if the user navigates to the URL Domain/Users/ by using the browsers navigation bar it matches the route and the user is taken to the user details page. I would like to append the ACME/jsmith/ onto the URL in this case.
The research I have done so far indicates I might have to implement a custom route object by deriving from RouteBase and implementing the GetRouteData and GetVirtualPath methods but I do not know where to start with this (the msdn documentaiton is very thin).
So what I would like to know is:
Is there a way of achieving this without implementing a custom route?
If not, does anyone know of any good resources to get me started implementing a custom route?
If a custom route implementation is required, how does it get at the information which presumably has to be loaded from the database? Is it OK to have a service make database calls in a route (which seems wrong to me) or can the information be passed to the route by the MVC framework?
It's actually pretty simple. Since the title is just there for SEO reasons you do not need to get to the actual question, so the Question controller (in SO case) will load the correct question based on the id (in the URL) and redirect the user with a 301 status code.
You can see this behavior with any web inspector
You could do it client-side with Javascript:
history.pushState({}, /* Title Here */, /* URL Here */ );
Only downside is not all browsers support it.

How to change url for MVC4 WebSite?

I need to change the URL on my address bar.
I looked for URL Rewrite but as far as I've seen it works for a request like this:
url.com/mypage.aspx?xp=asd&yp=okasd
and transforms that into:
url.com/mypage/asd/okasd
http://www.iis.net/downloads/microsoft/url-rewrite
That's not my scope. I already have MVC Routes working with url.com/mypage. The problem is that when I type that URL I am redirected (that's the behavior I want) to url.com/otherpage/actionX?param=value. The problem is that I still want the address bar to show url.com/mypage. Will URL Rewrite work for that?
I am asking because I don't know if it will work since it's an internal redirect (RedirectToAction) instead of a 'regular' access.
In case someone wonders why I can't make a route for that, as explained in my question I alread have one rule for that url.com/mypage that redirects to a 'router' which decides what action to call.
I've seen some questions, but I don't think they cover my specific problem:
MVC3 change the url
C# - How to Rewrite a URL in MVC3
UPDATE
This is my route:
routes.MapRoute(
"Profile", // Route name
"{urlParam}", // URL with parameters
new { controller = "Profile", action = "Router" } // Parameter defaults
);
Inside Router action I redirect to the correct action according to some validation done on urlParam. I need this behavior since each action returns a different View.
Updated my tags since I am now using MVC4
Thanks.
I once had to run a php site on a windows box. On the linux box it originally ran, it had a rewrite defined to make site process all request in only one php file (index.php).
I've installed and configured URL Rewrite with following parameters
Name : all to index.php
Match URL ------------------------------
Requested URL : Matches the Pattern
Using : Regular Expressions
Pattern : (.*)
Ignore Case : Checked
Conditions -----------------------------
Logical Grouping : Match All
Input : {REQUEST_FILENAME}
Type : Is Not a File
Action ---------------------------------
Action Type : Rewrite
Action Properties:
Rewrite Url : /index.php?$1
Append Query String : Checked
Log Rewritten URL : Checked
this makes all requests to site (except files like css and js files) to be processed by index.php
so url.com/user/1 is processed on server side as url.com/index.php?/user/1
since it works on server side client url stays same.
using this as you base you can build a rewrite (not a redirect).
Server.Transfer is exactly what you need, but that is not available on MVC.
On the MVC world you can use the TransferResult class defined in this thread.
With that... you add code to your ROUTE action that process the urlParam as always and instead of "redirecting" (RedirectToAction) the user to a new URL, you just "transfer" him/her to a new action method without changing the URL.
But there it a catch (I think, I have not tested it)... if that new page postbacks something... it will NOT use your router's action URL (url.com/mypage), but the real ACTION (url.com/otherpage)
Hope it helps.
In my opinion,you can try following things:
Return EmptyResult or RedirectResult from your Action method.
Also,you need to setup and construct outbound route for URL that you required.
Secondly, if these didn't work,the crude way to handle this situation is with Div tag and replacing the contents of Div with whatever HTML emitted by Action method. I am assuming here that in your problem context, you can call up jquery ajax call.
Hope this Helps.
The problem you have is that you redirect the user ( using 302 http code) to a new location, so browser ,reloads the page. you need to modify the routes to point directly to your controller. The route registration should be routes.MapRoute("specific", "my page", new { controller = "otherpage", action="actions", param="value"}).
This route should be registered first

.NET MVC3 : Create Virtual file URL and redirect to controller

Suppose I got a MVC3 website with a URL like this:
http://www.anything.com/feed.xml
The trick is, the file feed.xml does not really exist, it will be generated dynamically by a controller at runtime. That way, it will be transparent to people. Any idea how should I bind the controller to the virtual URL?
Thank you very much.
Is this what you need?
routes.MapRoute("", "feed.xml", new { controller = "Feed", action = "Index" });
Create custom route and return File result from action. Look at this links:
Back to Basics: Dynamic Image Generation, ASP.NET Controllers,
Routing, IHttpHandlers, and runAllManagedModulesForAllRequests
(in your situation no need for custom RouteHandler)
ASP.NET MVC Uploading and Downloading Files (how return file)

MVC route where path exists

I'm trying to create a route that will caption a url like
http://mysite.com/tech/
but I also have an actual directory under the site that is /tech, which contains other static resources. Not my design choice, but I'm migrating an older site to mvc and dont want to break a bunch of very old links.
My route seems to keep failing, and what I can figure is that the server sees this directory and is trying to serve up the default file from it... but none is found, and it fails.
I'd like it to use my route instead and let me serve up my view. Any best practices for this?
UPDATE:
I tried using RouteExistingFiles = true, which made this work, but then I had issues with files which existed inside these directories. (for instance: http://mysite.com/tech/image.jpg). I started adding IgnoreRoute entries for all the various file types that I wanted ignored (ie .jpg files..), but this turned into a big mess. There's gotta be a better way?
You need to set the RouteExistingFiles property of your RouteCollections object to true. This will make your routing override the physical location if a clash is found.
routes.MapRoute("Default", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
routes.RouteExistingFiles = true;
To selectively ignore files/extensions you can use :
routes.IgnoreRoute("{*staticfile}", new { staticfile = #".*\.(css|js|gif|jpg)(/.*)?" });
Another approach which might help if you don't like the ignoreRoute approach, is to extend the ViewEngine and override the file exists implementaion with your own rules.
public class MyViewEngine : RazorViewEngine
{
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
//Some Logic to check for file
}
}
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());

Categories

Resources