user972940
user972940

Reputation:

Routes not working properly in asp.net mvc multilanguage app

I have two routes in my asp.net mvc app

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Default",
                url: "{culture}/{controller}/{action}/{id}",
                defaults: new { culture = CultureHelper.GetDefaultCulture(), controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

            routes.MapRoute(
                name: "Default no language",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

When i enter url http://localhost/galleryin browser, i'm redirected to http://localhost/, when i try http://localhost/gallery/index, i'm geting 404 error

 The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly.

Requested URL: /gallery/index

when i add any implemented language to url (http://localhost/en/gallery), app is working.

I'm new to routes and webapp programing, so i would appriciate if anyone could help me with this with detailed explanation how to fix this.

Upvotes: 1

Views: 322

Answers (1)

Luke
Luke

Reputation: 23690

The problem is that you have two routes, but the first route will always bind because it can't tell the difference between {culture}/{controller}/{action}/{id} and {controller}/{action}/{id} when binding /gallery - is it a culture or just a controller name?

There's no way to tell without checking, it will just bind the first match.

If you remove the default values then it might work, but you will always have to specify the action and controller:

routes.MapRoute(
    name: "Default",
    url: "{culture}/{controller}/{action}/{id}",
    defaults: new { culture = CultureHelper.GetDefaultCulture(), 
    /* controller = "Home", action = "Index",*/ id = UrlParameter.Optional }
);

{culture}/{controller}/{action}/{id} will now bind:

  • /en/gallery/actionName
  • /en/controllerName/actionName
  • /en/controllerName/actionName/idValue

and {controller}/{action}/{id} will bind:

  • /gallery
  • /gallery/actionName
  • /gallery/actionName/idValue

Your other option is to create a custom route constraint to validate that the first value is a valid culture value:

public class IsValidCultureConstraint : IRouteConstraint
{
    public bool Match
        (
            HttpContextBase httpContext, 
            Route route, 
            string parameterName, 
            RouteValueDictionary values, 
            RouteDirection routeDirection
        )
    {

        if (string.IsNullOrWhiteSpace(values["culture"]))
        {
            return this.IsValidCulture();
        }

        return false;
    }

    private bool IsValidCulture(string cultureString)
    {
        //
        // Logic here to check valid culture(s)
        //

        // Example
        if (String.Equals(cultureString.Trim(), "en", 
               StringComparison.OrdinalIgnoreCase))
        {
            // English is valid
            return true;
        }

        return false;
    }
}


routes.MapRoute(
    name: "Default",
    url: "{culture}/{controller}/{action}/{id}",
    defaults: new { culture = CultureHelper.GetDefaultCulture(), 
    id = UrlParameter.Optional,
    constraints: new { culture = new IsValidCultureConstraint() }
});

With that in place, it will only ever match the first route if the value entered into the URL is a valid culture. Of course, you will need to provide the logic to perform the check properly.

Upvotes: 1

Related Questions