2

I am trying to shorten my URLs on my MVC app (if this is even possible). I use areas on my web app. Currently I got URL slugs working, this seite helped a lot: http://www.itorian.com/2016/12/slug-url-in-mvc.html

Here is part of my working area registration, note the {articleTitle} is the slug.

public override void RegisterArea(AreaRegistrationContext context)
        {       
            context.MapRoute(
                "Discussion_default",
                "Discussion/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );

            context.MapRoute(
                "View_Article",
                "Discussion/Articlev1/Details/{id}/{articleTitle}",
                new { controller = "Articlev1", action = "Details", id = UrlParameter.Optional, articleTitle = "" }
            );
        }

This generates a long URL (www.sitename.com/Discussion/Articlev1/Details/id/cool-html-article)

Is there a way I can have it generate and use something like:

www.sitename.com/Articlev1/id/cool-html-article

or even just have it omit the details portion like this:

www.sitename.com/Discussion/Articlev1/id/cool-html-article

I have tried a few things but none of them work, either generating 404 errors or 400.1 errors. Is this possible?

Machavity
  • 30,841
  • 27
  • 92
  • 100
dave317
  • 754
  • 2
  • 12
  • 30

1 Answers1

2

The way you have your routing setup, the View_Article route will never be hit unless you are generating the URL since the URL /Discussion/Articlev1/Details/id/cool-html-article will match the Discussion_default route.

First of all, put them in the correct order (from most specific to least specific):

public override void RegisterArea(AreaRegistrationContext context)
{
    context.MapRoute(
        "View_Article",
        "Discussion/Articlev1/Details/{id}/{articleTitle}",
        new { controller = "Articlev1", action = "Details", id = UrlParameter.Optional, articleTitle = UrlParameter.Optional }
    );

    context.MapRoute(
        "Discussion_default",
        "Discussion/{controller}/{action}/{id}",
        new { action = "Index", id = UrlParameter.Optional }
    );
}

From there, it is easy to change the first URL to whatever you like, as long as you take care to ensure there are no URL conflicts in your entire configuration.

public override void RegisterArea(AreaRegistrationContext context)
{
    context.MapRoute(
        "View_Article",
        "Discussion/Articlev1/{id}/{articleTitle}",
        new { controller = "Articlev1", action = "Details", id = UrlParameter.Optional, articleTitle = UrlParameter.Optional }
    );

    context.MapRoute(
        "Discussion_default",
        "Discussion/{controller}/{action}/{id}",
        new { action = "Index", id = UrlParameter.Optional }
    );
}

NOTE: It also probably doesn't make sense to have a View_Article URL that doesn't have an id, so you shouldn't make id = UrlParameter.Optional in that case.

public override void RegisterArea(AreaRegistrationContext context)
{
    context.MapRoute(
        "View_Article",
        "Discussion/Articlev1/{id}/{articleTitle}",
        new { controller = "Articlev1", action = "Details", articleTitle = UrlParameter.Optional }
    );

    context.MapRoute(
        "Discussion_default",
        "Discussion/{controller}/{action}/{id}",
        new { action = "Index", id = UrlParameter.Optional }
    );
}

Of course, this means if you have a controller in your application named ArticleV1Controller, the View_Article route will match (and set the id parameter to the action you have passed in the URL). If you can't live with having the /Details segment hard coded into the URL, then you will need to differentiate it another way, such as using a route constraint. The following assumes your ID must be all digits:

public override void RegisterArea(AreaRegistrationContext context)
{
    context.MapRoute(
        name: "View_Article",
        url: "Discussion/Articlev1/{id}/{articleTitle}",
        defaults: new { controller = "Articlev1", action = "Details", articleTitle = UrlParameter.Optional },
        constraints: new { id = @"\d+" }
    );

    context.MapRoute(
        name: "Discussion_default",
        url: "Discussion/{controller}/{action}/{id}",
        defaults: new { action = "Index", id = UrlParameter.Optional }
    );
}
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Thanks NightOwl, I'm going to try it now. What should I make it instead of URL.optional? URL.Required? - OOps, ignore this question, I see the second one addresses this – dave317 Jan 17 '18 at 16:34
  • Now I have a problem where my create and edit methods dont work, I'm guessing its because those action/controller URLs match the action/id format? Is there a good way around this? – dave317 Jan 17 '18 at 16:46
  • Would it create too many problems if I created an "ignoreroutes" in my RouteConfig and ignored sitename.com/Articlev1/* ? Going to play around with that now... – dave317 Jan 17 '18 at 16:49
  • Thanks again. I ended up using an ignoreroute on my RouteConfig but if this proves too buggy, then I will use the constraints instead. I didn't even know about Constraints. So far so good on testing. – dave317 Jan 17 '18 at 17:14
  • IgnoreRoutes is to block routes from matching. For example, by default `/`, `/Home`, and `/Home/Index` all work, but if you setup `IgnoreRoute("Home/Index")` it will return a 404 not found instead of the home page. `IgnoreRoute` has no effect on whether routes conflict, though. – NightOwl888 Jan 17 '18 at 17:14