Area routing breaks in MVC6 - asp.net-core

In previous versions of ASP.NET, and using MVC 5, I could set up a route like this in my AreaRegistraion:
context.MapRoute(
"pfrecent",
"Forums/Recent/{page}",
new { controller = ForumController.Name, action = "Recent", page = 1 },
new[] { "PopForums.Controllers" });
This would route /Forums/Recent to the forum controller and its recent action. However, I can't figure out how to make it work in ASP.Net 5/MVC 6. I've added [Area("Forums")] to the controller class, and used this route from the Startup class:
routes.MapRoute(
"pfrecent",
"Forums/Recent/{page}",
new { controller = ForumController.Name, action = "Recent", page = 1 });
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
However, it resolves to /Forums/Forum/Recent?page=1. The intention is to continue using /Forums/Recent.

We are using this for enabling areas in MVC 6:
// Configure MVC routing
app.UseMvc(routes =>
{
// Areas support
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
// Default routing
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
The first route is for areas, second is for main content.

On Startup.cs
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "Business",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
})
on Controller
[Area("Business")]
public class DemoController : Controller
{
public IActionResult Index()
{
return View();
}
}

Place this in your Startup.cs file at the TOP of the routes list:
routes.MapRoute(
name: "forumsAreaRoute",
template: "Forums/{action}/{page?}",
defaults: new {area = "Forums", controller = "Forum", action = "Recent", page = 1});
Your Forum controller should look like this:
[Area("Forums")]
public class ForumController : Controller
{
// GET: /<controller>/
public IActionResult Recent(int? page)
{
// Do action stuff here
}
}
This solution will satisfy a url of http://somedomain.com/Forums/Recent/1 and return the first page.

I hate answering my own questions, but after getting back to this, and looking at source and experimenting, I found that you have to specify the area in the route mapping, as well as the HtmlHelpers (I'm assuming this will be true for tag helpers as well, but I haven't gone that far yet in conversion.) So the route mapping has to look like this:
routes.MapRoute(
"pfrecent",
"Forums/Recent/{page?}",
new { controller = ForumController.Name, action = "Recent", page = 1, Area = "Forums" }
);
The important part is the Area property in the route value object. The HtmlHelper has to look like this, also naming the Area:
#Html.ActionLink(PopForums.Resources.Recent, "Recent", ForumController.Name, new { page = 1, Area = "Forums" }, null)
It causes the URL for the recent page to be /Forums/Recent as expected. As best I can tell, setting routes in a derived AreaRegistration class in MVC5 set the area value on the routes for you. Having [Area("Forums")] on your controller class (which I already had) seems to assume the previous role of area registration.

Related

How to create numeric routes in ASP.NET Core MVC

I want to have a URL path pattern like this:
http://example.com/15/232
Both first segment and the second segment are composed of integer values. In case this pattern is not met, I want routing to fallback to the default {controller=Home}/{action=Index} pattern.
How can I achieve this?
What I'd probably do is modify your startup.cs to reflect your new route like this:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "segment",
template: "{segment1:int}/{segment2:int}", //<-- Matches /15/232
defaults: new { controller = "Home", action = "Segment" }
);
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"
);
}
The new route would just point to a single controller and action, and from there, you return the appropriate View for the content based on the path.
public IActionResult Segment(int segment1, int segment2)
{
return View()
}

MapSpaFallbackRoute on Startup ASP .NET Core

I have an Areas on Net Core App named Admin, on MapSpaFallbackRoute setting on startup, I want to set like this,
app.UseMvc(routes =>
{
routes.MapSpaFallbackRoute(
name: "spa-fallback-admin",
defaults: new { area="Admin", controller = "Home", action = "Index" });
});
is this the correct way to define MapSpaFallbackRoute? I doubt MapSpaFallbackRoute have attributes area,
I have been try this, and my apps return 404(not found).
so, what the correct way to define MapSpaFallbackRoute, I want using HomeController on Admin area, with Index action
It is my complete code, I want to request with path admin, controller on admin areas should be handle that.
app.MapWhen(context => context.Request.Path.Value.StartsWith("/admin"), builder =>
{
builder.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{area=Admin}/{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback-admin",
defaults: new { area="Admin", controller = "Home", action = "Index" });
});
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
routes.MapRoute(
name: "areas",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
});
thanks for your help
What MapSpaFallbackRoute does is allows defining default values for route parameters to handle 404 cases.
Now to your question: yes, MVC routing (both attribute/convention) supports {area} as route parameter and so you can write above code to define a default value.
You didn't show your routing setup, so I assume that your main problem is that you haven't specified {area} parameter in your route template.
For example, if consider convention routing, the following should work:
app.UseMvc(routes => {
routes.MapRoute("default", "{area}/{controller}/{action}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback-admin",
defaults: new { area="Admin", controller = "Home", action = "Index" });
});
For updated question:
Try to use .UseWhen instead of .MapWhen:
app.UseWhen(context => context.Request.Path.Value.StartsWith("/admin"), builder =>
{
after searching for this issue I realize Dotnet core 3 doesn't support MapSpaFallbackRoute method so to fix this problem you should add the Spa package manually use this command on your terminal Dotnet
add package Microsoft.AspNetCore.SpaServices --version 3.1.16
you can check the version at this link
NuGet Gallery

// GET: /Article/{text} in MVC?

I have a controller named ArticleController with an Index method that returns the Article view. This works.
However, I'd like to be able to process any text after Article/ in the URL such as Article/someText Article/fileNanme Article/etc
I thought this would be straightforward by implementing the following:
// GET: /Article/{text}
public ActionResult Index(string someText)
{
...
}
This doesn't work. Any ideas?
Update:
See routes:
routes.MapRoute(
name: "Articles",
url: "Article/{*articleName}",
defaults: new { controller = "Article", action = "Article", id= UrlParameter.Optional }
,
constraints: new { therest = #"\w+" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller="Home", action = "Index", id = UrlParameter.Optional }
);
See ArticleController methods:
public ActionResult Index()
{
...
}
public ActionResult Article(string articleName)
{
...
}
You can add a catch-all parameter to the route like this
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{*therest}",
defaults: new { controller = "Home", action = "Index" }
);
Notice the asterisk? This marks therest as a "catch-all" parameter, which will match all remaining segments in the URL.
In your action, you would have
public ActionResult Article(string therest)
{
/*...*/
}
This works even for URLs like "Home/Article/This/Is/The/Rest", in which case therest will have the value "This/Is/The/Rest".
If you want to leave out the controller part of the URL completely, you would have
routes.MapRoute(
name: "Default",
url: "Article/{*therest}",
defaults: new { controller = "Home", action = "Index" }
);
which will match URLs like "Article/ThisIs/JustSomeText".
If you want therest to at least contain something, you might add a routing constraint:
routes.MapRoute(
name: "Default",
url: "Article/{*therest}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { therest = #"\w+" }
);
The constraint is a regular expression that therest must match for the route to match.
Stephen Walther has a nice article on routing and catch-all parameters.
Stephen Walther, again, has an article on routing constraints here.
If you are using standard routing change parameter name from someText to id. Otherwise you have to create custom routing for this parameter
You need to define a route in order to use the url you mentioned. For the latest MVC4, routes file exists in this directory App_Start/RouteConfig.cs
add this new route about the the default route.
routes.MapRoute(
name: "Custom",
url: "Article/{someText}",
defaults: new { controller = "Article", action = "Index" }
);
Try loading your url now. It should work now.

How to define default action parameters in ASP.NET MVC 4

I am following Pluralsight videos to learn MVC4.
While learning about default values for action parameters I have defined the following setting inside RouteConfig.cs
routes.MapRoute(
name: "cuisine",
url: "cuisine/{name}",
defaults: new { controller="cuisine", action="search", name=""});
I have created CuisineController with Search() as the action method like below:
public ActionResult Search(string name="India")
{
var message = Server.HtmlEncode(name);
return Content(message);
}
As per the video I have seen, if nothing is passed in the URL then India should come as output.
But, I am getting empty string.
Where I am doing wrong?
You have to use UrlParameter.Optional
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{name}", // URL with parameters
new { controller = "Home", action = "Search", name = UrlParameter.Optional } // Parameter defaults
);
public ActionResult Search(string name = "India")
{
var message = Server.HtmlEncode(name);
return Content(message);
}
This displayed "India" perfectly in page.

ASP.NET MVC URL Routing with parameters in the middle

I have a controller called Quote, with an Index method that requires a requestId parameter. The URL appears as such
/Quote/{requestId}.
Additionally, I have a method called ApplicantInfo which is specific to the quote and routes as such
/Quote/{requestId}/ApplicantInfo.
But when I use the Url.Action helper like so
#Url.Action("Index","Quote",new { requestId = {requestId}})
It gives me a url of
/Quote/ApplicantInfo?requestId={requestId}
which does not route correctly.
Obviously, I can manually create the URL string, but before I throw in the towel I wanted to know if I was missing something, for instance an override to the Url.Action method that will output the correct Url.
TIA
ROUTES
routes.MapRoute(
"QuoteInfo",
"Quote/{requestid}",
new { controller = "Quote", action="Index" });
routes.MapRoute(
"QuoteApplicant",
"Quote/{requestid}/ApplicantInfo",
new { controller = "Quote", action = "ApplicantInfo" });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action="Index", id = UrlParameter.Optional });
I was able to get do something similar to this like this
Route
Define a route in Global.asax.cs or whereeve you override your routes
routes.MapRoute(
"Organization_default",
"{controller}/{requestId}/{action}",
new {
controller = "home",
action = "index",
requestId = "default",
id = UrlParameter.Optional
}, null);
Controller
public ActionResult Question(string requestId)
{
ViewData["value"] = requestId;
return View();
}
View
#{
ViewBag.Title = "Index";
var value = ViewData["value"];
}
<h2>Stackoverflow #value</h2>
#Html.ActionLink("Click Here",
"question", "StackOverflow", new { requestId ="Link" }, new{ #id="link"})
Screenshot
screenshot of how the links appear with this route, I defined the
Catch
You CANNOT have another route as {controller}/{action}/{key} defined before your custom this route. If you do the other route override your custom route.
If you need both the routes to co-exist then you would have to define a separate Area and define your custom route overriding RegisterArea
public override void RegisterArea(AreaRegistrationContext context)
{
//..custom route definition
}