It works now like localhost/Controller/Action
but I want it like localhost/MainFolder/SubFolder/Controller/Action
because I need to get my MainFolder and SubFolder name; like
RouteData routeData = htmlHelper.ViewContext.RouteData;
string currentAction = routeData.GetRequiredString("action");
string currentMainFolder = routeData.GetRequiredString("mainFolder");
string currentSubFolder = routeData.GetRequiredString("subFolder");
MyViewEngine Codes;
public class MyViewEngine: RazorViewEngine
{
private static string[] NewViewFormats = new[] { "~/Views/MainFolder/SubFolder/{1}/{0}.cshtml" };
public MyViewEngine()
{
base.ViewLocationFormats = base.ViewLocationFormats.Union(NewViewFormats).ToArray();
}
}
My RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Panel", action = "Index", id = UrlParameter.Optional }
);
ViewEngines.Engines.Add(new MyViewEngine());
}
My Views Folder;
My Controllers Folder;
MVC Routes are not filesystem-based like old-style ASP.NET sites. All the controllers are registered at compile and the route just picks the one with the matching name. Where the controller lives on your filesystem is irrelevant.
Related
UPDATE: It works fine is I add another parameter. I have no idea why.
Not sure what I am doing wrong, this route looks the same as the other routes, I've looked at all the other stackoverflow posts on the subject and everything looks up to snuff. Maybe someone can see something I can't?
Error: action parameter can't be null
The url: http://localhost:1319/EntryHistory/Entry/B2AAAA4A-B174-4C28-924F-A3B2027DD745
EntryHistoryController.cs:
public class EntryHistoryController : Controller
{
public ActionResult Entry(string entryId)
{
Guid parsedEntryId = Guid.Parse(entryId);
var history = new EntryHistoryService().BuildEntryHistoryViewModel(parsedEntryId);
return View(history);
}
}
RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
...
EntryHistoryRoutes(routes);
...
}
private static void EntryHistoryRoutes(RouteCollection routes)
{
routes.MapRoute(
"revisionhistory",
"entryhistory/entry/{entryId}",
new
{
controller = "entryhistory",
action = "entry",
entryId = UrlParameter.Optional
}
);
}
add attribute routing
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
...... your routes
//convention-based routes
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
and action
[Route("~/entryhistory/entry/{entryId}")]
public ActionResult Entry(string entryId)
I would like to create a dynamic routing to a URL like following:
http://localhost:51577/Item/AnyActionName/Id
Please note that the controller name is static and doesn't need to be dynamic. On the other hand, I need to have the action name part dynamic so that whatever is written in that part of URL, I would redirect the user to the Index action inside of Item controller.
What I have tried so far is:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Items",
"Item/{action}/{id}",
new { controller = "Item", action = "Index", id = UrlParameter.Optional });
}
And when I build my app I get a following error:
The resource cannot be found.
Edit:
Here is my Global.asax file and the routeconfig.cs file:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
And here's the content of the RouteConfig.cs file with the answer that #Nkosi provided:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Items",
url: "Item/{id}/{*slug}",
defaults: new { controller = "Item", action = "Index", slug = UrlParameter.Optional }
);
}
}
What you are referring to in your question is called a slug.
I answered a similar question here for web api
Web api - how to route using slugs?
With the slug at the end the route config would look like this
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Items",
url: "Item/{id}/{*slug}",
defaults: new { controller = "Item", action = "Index", slug = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
which could match an example controller action...
public class ItemController : Controller {
public ActionResult Index(int id, string slug = null) {
//...
}
}
the example URL...
"Item/31223512/Any-Item-Name"
would then have the parameters matched as follows...
id = 31223512
slug = "Any-Item-Name"
And because the slug is optional, the above URL will still be matched to
"Item/31223512"
Introduction:
I develop multilingual web application. Admin can create new languages (this information is stored in database). So languages are not hardcoded somewhere in code. The user's preferred language is stored in browser cookie.
Now i want to configure url routes from this:
www.host.com/home/about
to this:
www.host.com/{lang}/home/about
RouteConfig.cs looks like:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
foreach (Route r in routes)
{
r.Url = "{lang}/" + r.Url;
if (r.Defaults == null)
r.Defaults = new RouteValueDictionary();
r.Defaults.Add("lang", ServiceLocalization.GetLanguageFromBrowserCookie());
}
}
}
Parameter {lang} is added to every request url. I need to set default value from cookie. Service method GetLanguageFromBrowserCookie() uses HttpContext.Current.Request.Cookies object to access cookies sent by the client.
But HttpContext.Current.Request object is not accessible at this stage of request-handling pipeline. And i get this error: "Request is not available in this context"
Is there a way to fetch MapRoute parameter with cookie value?
Ok. I figured out how to do this. We need to create custom RouteHandler to process request url with extra logic.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
).RouteHandler = new CustomRouteHandler();
}
}
public class CustomRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var route = (Route)requestContext.RouteData.Route;
if (!route.Url.Contains("{lang}"))
route.Url = "{lang}/" + route.Url;
if (route.Defaults == null)
{
route.Defaults = new RouteValueDictionary();
route.Defaults.Add("lang", ServiceLocalization.GetLanguageFromBrowserCookie().CodeName);
}
else
{
route.Defaults["lang"] = ServiceLocalization.GetLanguageFromBrowserCookie().CodeName;
}
return base.GetHttpHandler(requestContext);
}
}
I create CustomRouteHandler and pass it to Default route. In GetHttpHandler method we can access current HttpRequest and get any data from request.
I want to add a new route to my Web Api, which will read various ids and then filter a bunch of books.
So the final url should read something like http://localhost/api/books/1/1/1/1
Now I have added a route to my RouteConfig as follows :-
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "BookFilter",
url: "api/books/{author}/{title}/{genre}/{isbn}",
defaults: new { controller = "Books", action = "BookFilter" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
I also added the following in my BooksController:-
[HttpGet]
public IQueryable<BookDTO> BookFilter(int authorId, int titleId, int genreId, int isbn)
{
//filter books here
return db.Books.ProjectTo<BookDTO>();
}
However when I try to reach the page, I get a 404.
What do I need to do to reach my page?
Thanks for your help and time
Web API and MVC are independent frameworks which each have separate types. The likely reason your route is not working is that you are confusing the two. Specifically, for it to work as you configured, you would need an MVC controller (that is, a controller that inherits System.Web.Mvc.Controller).
So, assuming you want to go with Web API as your question would indicate, you first need to ensure the correct definition of your controller. It should inherit from System.Web.Http.ApiController.
public class BooksController : ApiController
{
[HttpGet]
public IHttpActionResult BookFilter(string author, string title, string genre, string isbn)
{
return Ok("Successful result");
}
}
Next, you need to put your routing in the WebApiConfig.cs file, not in the RouteConfig.cs file. Don't forget to remove your route from the RouteConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "BookFilter",
routeTemplate: "api/books/{author}/{title}/{genre}/{isbn}",
defaults: new { controller = "Books", action = "BookFilter" });
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
You also need to ensure that the call to GlobalConfiguration.Configure(WebApiConfig.Register); is in your application startup path (by default in Global.asax).
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
Make sure to use the same parameter name in your action (eg. change author to authorId):
Optionally, you can also specify default values for these parameters at your RouteConfig, as follows:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "BookFilter",
url: "api/books/{authorId}/{titleId}/{genreId}/{isbn}",
defaults: new { controller = "Books", action = "BookFilter", authorId= UrlParameter.Optional, titleId = UrlParameter.Optional, genreId = UrlParameter.Optional, isbn = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Controller:
[HttpGet]
public IQueryable<BookDTO> BookFilter(int authorId, int titleId, int genreId, int isbn)
{
//filter books here
return db.Books.ProjectTo<BookDTO>();
}
I'm not sure if this is possible using mvc routing, I haven't been able to find a similar example.
I have about 5~ controller actions that are the same method, so I'd like to refactor them into a single action. I'd like to pass an enum value to the controller to tell it what path it should pass to lower layers.
Example:
public ActionResult ViewPage(int id, PageEnum page) {
var model = MyService.GetModelForTemplate(id, page);
return ("ViewPage", model);
}
Then the user could access this either through /PagesTypeOne/ViewPage/, or /PagesTypeTwo/ViewPage/. Both routes leading to the same endpoint.
Route table attempt:
routes.MapRoute(
name: "typeOne",
url: "PagesTypeOne/{action}/{id}",
defaults: new { controller = "Pages", action = "ViewPage", id = UrlParameter.Optional, page = PageEnum.TypeOne, }
);
routes.MapRoute(
name: "typeTwo",
url: "PagesTypeTwo/{action}/{id}",
defaults: new { controller = "Pages", action = "ViewPage", id = UrlParameter.Optional, page = PageEnum.TypeTwo, }
);
This obviously isn't working.
Is there a way I can do something like this? It would make my code much more concise.
screen != page, so if the property on the anonymous type matches the parameter it will work:
public ActionResult ViewPage(int id, PageEnum screen) {
var model = MyService.GetModelForTemplate(screen);
return ("ViewPage", model);
}
Updated: Created a empty application and it work flawlessly:
namespace MvcApplication6
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Test1",
"PagesTypeOne/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional,
page = PageEnum.PageOne } // Parameter defaults
);
routes.MapRoute(
"Test2",
"PagesTypeTwo/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional,
page = PageEnum.PageOne } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
public enum PageEnum
{
Undefined,
PageOne,
PageTwo
}
Controller:
namespace MvcApplication6.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult ViewPage(int id, PageEnum page)
{
var debug = 1; // break point
return new EmptyResult();
}
}
}
you can make it completely dynamic with one route definition:
// the route must be defined as the first route
routes.MapRoute(
name: "typeTwo",
url: "{page}/ViewPage/{id}",
defaults: new { controller = "Pages", action = "ViewPage", id = UrlParameter.Optional },
new { page= getPageTypes() }
);
the getPageTypes method:
private static string getPageTypes()
{
var pageTypes = Enum.GetNames(typeof(PageEnum));
return string.Join("|", pageTypes );
}
but PagesTypeOne/ViewPage/4 part must match the enum's name.