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.
Related
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
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());
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.
I find myself in a difficult situation. We're working on an ASP.NET MVC 2 application which is comprised of multiple sections. It is a design goal to have these sections span across several subdomains. Each subdomain will have its own controller.
The challenge is that our hosting provider's control panel allows two forms of redirection for subdomains, and neither of them seem to fit the bill. The choices are:
Redirecting to URL. Choice is given whether to redirect to an exact destination or a destination relative to the request URL.
Redirecting to a specific folder in my hosting space.
I'll try to illustrate the intended behaviour. Assuming the default route is {controller}/{action}/{id}, I'd like the URL http://subdomain.welcome.com/a/b be handled by the MVC Application like http://welcome.com/subdomain/a/b.
The URL redirection could solve this problem, except for the fact that the user sees a URL change occur in the browser. We don't want the client to see the redirection occur.
Redirecting to our MVC apps root folder doesn't work at all. The app doesn't pick up the request and a 4xx error gets passed back by IIS.
edit:
In the interest of finding an answer, I'll simplify this a bit. The "redirect to URL" doesn't do what I want so that leaves redirecting to a folder.
If I'm redirecting a subdomain to the root folder of my MVC App and IIS wont pick up the requests, is this a limitation of IIS or my provider?
Can you make your hosting website host headers respond to *.mydomain.com? Meaning, can your website take request for any sub domain of your primary domain? If so, then reference this post on how to handle subdomain routing in MVC apps and you should be good to go.
I would update the code in the post to this however, to make the code more succinct. In any case, make sure you have your 404 errors in place for people attempting to go to subdomains that don't exist.
public class ExampleRoute : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var url = httpContext.Request.Headers["HOST"];
var index = url.IndexOf(".");
if (index < 0)
return null;
var subDomain = url.Substring(0, index);
var routeData = new RouteData(this, new MvcRouteHandler());
routeData.Values.Add("controller", subdomain); //attempts to go to controller action of the subdomain
routeData.Values.Add("action", "Index"); //Goes to the Index action on the User2Controller
return routeData;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//Implement your formating Url formating here
return null;
}
}
Not sure if this is overkill (this is actually used to serve pages from a zip file or resource file, etc), BUT... perhaps you could use a Virtual Path Provider?..
Implement a class that inherits from VirtualPathProvider, and register it in global startup like so:
HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
Then implement a class that inherits from VirtualFile and serve it from the GetFile() override in your virtual path provider implementation:
public override VirtualFile GetFile( string virtualPath )
{
if( IsVirtualFile(virtualPath) )
return new MyVirtualFile(virtualPath);
return base.GetFile(virtualPath);
}
Note: IsVirtualFile is a function you would have to implement, based on the rules you have regarding the URL format, etc.
In my current project we have a notification system. When an oject is added to another objects collection, an email is sent to those who are subscibed to the parent object. This happens on the object layer and not in the View or Controller.
Here's the problem:
Although we can say who created what with what information in the email, we cannot embed links to those objects in the email because in the object layer there is no access to a UrlHelper. To construct a UrlHelper you need a RequestContext, which again does not exist on the object layer.
Question:
I want to make a helper class to create the url's for me. How can I create an object that will generate these urls without a request context? Is it possible?
The problem is compounded by the fact that you don't want a relative URL in an email, you want an absolute email so you need to hard-code the domain too because there is no request to grab it from.
Another factor is that emails can outlive the current site structure by months or years so you need a kind of permalink, and thus a way to associate multiple Urls with a single action (additional routes). This latter issue is also a factor in SEO where you don't want to leave any page behind.
For now a static method on your controller UrlToActionX(params) sitting next to the method ActionX seems like the simplest workaround. All it does is the appropriate string.Format(...) on the id's of the strongly-typed parameters to generate the permanent Url. Add a static domain on the front, or a domain from the user object (since you know which domain they visit when they come to your site) and you have your email link.
It's not ideal but at least you now have only one place to maintain the Url generation.
IMHO: When it comes to permanent links to a changing web site sometimes it's better to rely on "configuration over convention". :-)
I'm not aware of a way to do this, you MUST have access to the routes at the very least to make your own helper. Unless your business objects know about the registered routes, you can't get away from doing some hard-coding.
Here is how you might limit the hard-coding of urls though...
Code in a url with all the relevant bits in your object's methods..
class Event
{
public void SendEmail()
{
var url = string.Format("http://myurl.com/r/Event?eventId={0}", EventId);
//send emails...
}
}
Note the /r/Event piece of the url. This would be a map to a RController that would be responsible for taking arbitrary, made-up links and sending a 301 Permanent Redirect and going through the route engine to create a real url using the current routes. This way you are only hard-coding a utility controller url and not to the ever evolving controller actions of your real pages.
class RController : Controller
{
public ActionResult Event(int eventId)
{
Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
Response.RedirectLocation = Url.Action("Details", "Event", new { eventId = eventId });
return null;
}
public ActionResult Register(int eventId)
{
Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
Response.RedirectLocation = Url.Action("Register", "Event", new { eventId = eventId });
return null;
}
}
It just feels a bit better than hard-coding a bunch of different controllers/actions that you might decide to rename later. Think of it as your own little TinyUrl like service.
You could define an interface with a method that takes whatever information is necessary to create a URL (object ids or whatever) and returns a URL. Write an implementation of that interface that uses the UrlHelper to do this work, and then supply this to your object layer (ideally with an IoC container).
You could use:
VirtualPathUtility.ToAbsolute(string.Format("~/r/Event?eventId={0}", id))
to resolve the url. Still not nice though.