views:

184

answers:

2

I'm trying to create a simple site. Basically it has one Controller Home controller.

This controller has an action Index takes a string as argument (which is a directory) and uses that 'directory' to do its work.

I can't work out how to create a generic catch all route that will send every URL to this one Action.

Any URL combination could exist, and anything beyond the domain name should be the string.

http://<domain>/2008
http://<domain>/2008/09
http://<domain>/2008/09/Fred

Does anyone know how to do this? It would also be alright if all the values are passed as a list.

Is there a better way of doing this?

+4  A: 

Try this :

routes.MapRoute(
        "Default", "{*path}",
        new { controller = "Home", action = "Index" } 
    );

And the controller :

public class HomeController : Controller {
    public ActionResult Index(string path) {
        return View();
    }
}
çağdaş
If you're trying to pass in a directory with '/' this will not work
Yuriy Faktorovich
@Yuriy Faktorovich, can you give an example route? Do you mean like `site.com/foo/bar`? If so, it works with routes like that.
çağdaş
site.com/C:/test, broke for me, you also need a comma on line 2(after path)
Yuriy Faktorovich
`site.com/C:/test` is a `HTTP 400`, not `404`, though (because of the `:` character, I think). I wonder if we should ever try to work around that.
çağdaş
I think you're right, it is the only problem. So his solution would be to use yours without a :, I'll give you an upvote.
Yuriy Faktorovich
Thanks :) 15char
çağdaş
+1  A: 

My first thought is that is a 'bad idea'. If you can take anything they can throw at you for a URL, you're going to have to have a heck of a blacklist (or whitelist). There's so many openings. A better way would be to explicitly spell out those routes and how you allow parameters, and then have actions that deal with those accepted routes. You'd then have a generic-catch all route that would redirect to an error page.

It feels like you're trying to mix apples and oranges. ASP.NET MVC purposefully does away with the 'page' idea. There's really no reason to have directories for various users unless you're doing some sort of File I/O, and if that's the case then it can be abstracted to work in the ASP.NET MVC paradigm much more easily than you may think.

In ASP.NET MVC if you wanted to vary the information you're looking for based on the string passed (much like the string you passed), here's a 'safe' way of doing it:

Method #1 - Three Routes, Three Actions, Different Names

routes.MapRoute(
  "YearOnly",
  "{year}",
  new { controller = "Index", action = "ShowByYear" },
  new { year = @"\d{4}" }
);

routes.MapRoute(
  "YearAndMonth",
  "{year}/{month}",
  new { controller = "Index", action = "ShowByYearAndMonth" },
  new { year = @"\d{4}", month = @"\d{2}" }
);

routes.MapRoute(
  "YearMonthAndName",
  "{year}/{month}/{name}",
  new { controller = "Index", action = "ShowByYearMonthAndName" },
  new { year = @"\d{4}", month = @"\d{2}" }
);

And then you'd use the passed route values in your Controller Actions to determine how they see the data:

ShowByYear(string year)
{
    //Return appropriate View here
}

ShowByYearAndMonth(string year, string month)
{ 
    //Return appropriate View here
}

ShowByYearMonthAndName(string year, string month, string name)
{ 
    //Return appropriate View here
}

Method #2 - Suggested Method

routes.MapRoute(
  "YearOnly",
  "{year}",
  new { controller = "Index", action = "Show" },
  new { year = @"\d{4}" }
);

routes.MapRoute(
  "YearAndMonth",
  "{year}/{month}",
  new { controller = "Index", action = "Show" },
  new { year = @"\d{4}", month = @"\d{2}" }
);

routes.MapRoute(
  "YearMonthAndName",
  "{year}/{month}/{name}",
  new { controller = "Index", action = "Show" },
  new { year = @"\d{4}", month = @"\d{2}", name = "" }
);

Show(string year)
{
    //
}

Show(string year, string month)
{
    //Return appropriate View here
}

Show(string year, string month, string name)
{
    //Return appropriate View here
}

The beauty of this approach is that the MapRoute handles URL parsing; and keeps the riff-raff out. You can then set up a catch-all route that just throws an error. You'd rather have the some parsing done on the route side with regex than in your controller (in my opinion).

This keeps it down to three overloaded actions, and allows for cleaner and more 'MVC'-ish code.

George Stocker
I thought about that too. But then realized he says "basically it has one page", which would be a valid point to do what he explains in the question.
çağdaş
I would tend to agree. But after many years of putting it off I'm rebuilding my photo site. I want it to be as simple as possible. Really there only needs to be one page. I don't want a DB, and I want it to be all self discovering. I.e. I FTP a directory of photos within a directory structure and then jobs a done.Since folder directories cannot have / in them, the only case I need to handle is if a directory as represented by the URl does not exist. Simple.
Jordan
I've already edited my answer a few times; but I want to point out that you still want to check out the strings being passed to you and use either Linq-To-SQL or some sort of safe way of dealing with those strings to prevent things like SQL injection. Not 'all' the parsing should be done in the route, but determining what gets to your app should be done there.
George Stocker