1

I get intermittent problems with MVC routing attributes which stop working on our Azure hosted site.

Controller action and attributes look like this:

[AllowAnonymous]
[RequireHttps]
[RoutePrefix("{lang}")]
public class HomeController : LocalizationController
{

[...]

[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
    [AllowAnonymous]
    [Route("sl", Name = "SelectLanguage")]
    public ActionResult SelectLanguage(LanguageVersions? id)
    {

This is action is called from the View like this using BeginForm:

@using (Html.BeginForm("SelectLanguage", "Home", null, FormMethod.Post, new { @class = "form", role = "form" }))
    {

Or like this:

@Html.ActionLink(@Resources.Shared.LY_LEARN_SPANISH, "SelectLanguage", "Home", routeValues: new { id = CommonItems.LanguageVersions.SpanishVersion }, htmlAttributes: new { title = @Resources.Shared.LY_TO_SPANISH })

The last 2-3 days this has intermittently stopped working and the URL gets written without the attributes, i.e. becomes:

Home/SelectLanguage/SpanishVersion

Which gives a 404 error (and re-routes to error page).

After restarting the server, the attributes start working again. I've done this a few times, but the problems seem to reappear after 10-20 hrs. It also happens with other controller actions in a similar way.

Any ideas?

EDIT:

This is the Localization controller:

 public abstract class LocalizationController : Controller
{

    /// <summary>
    /// Gets/sets the current user culture.
    /// </summary>
    public string strLang { get; set; }

    /// <summary>
    /// Executed before an action is executed.
    /// </summary>
    /// <param name="filterContext">The current filter context</param>
    protected override void OnActionExecuting(ActionExecutingContext filterContext) {

        // Check if there is a route data language version.
        strLang = (string)ControllerContext.RouteData.Values["lang"];

        // Check if there is a logged in user...
        if (Request.IsAuthenticated == true)
        {
            // There is an ASP.NET User, get the culture from that User.
            var UserManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
            ApplicationUser myUser = UserManager.FindById(User.Identity.GetUserId());

            // If there is a different culture than the route, we must set the value from the User
            if (myUser.UserCulture != strLang)
            {
                strLang = myUser.UserCulture;

                // Update the filter context to re - route using the "lang" - field.
                SetFilterContext(filterContext);
            }
            else if ((myUser.UserCulture == strLang) && strLang == null)
            {
                // At rare occasions, both can be null. Then we use default and re route using default.
                strLang = "en";
                Thread.CurrentThread.CurrentCulture =
                Thread.CurrentThread.CurrentUICulture = new CultureInfo(strLang);

                // Set and update the User's culture as well.
                myUser.UserCulture = strLang;

                UserManager.Update(myUser);
                HttpContext.GetOwinContext().Get<ApplicationDbContext>().SaveChanges();
            }

        } else if (strLang == null)
        {
            // Get the language from the browser setting.
            var userLanguages = Request.UserLanguages;
            CultureInfo ci;
            if (userLanguages.Count() > 0)
            {
                try
                {
                    ci = new CultureInfo(userLanguages[0]);
                }
                catch (CultureNotFoundException)
                {
                    ci = CultureInfo.InvariantCulture;
                }
            }
            else
            {
                ci = CultureInfo.InvariantCulture;
            }
            strLang = ci.TwoLetterISOLanguageName;

            // Update the filter context to re-route using the "lang"-field.
            SetFilterContext(filterContext);
        }

        // Set culture...
        try
        {
            Thread.CurrentThread.CurrentCulture =
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(strLang);
        }
        catch (System.Exception e)
        {
            // Can't set this culture, select default...
            strLang = e.Message;
            strLang = "en";

            Thread.CurrentThread.CurrentCulture =
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(strLang);
        }

        base.OnActionExecuting(filterContext);
    }


    private void SetFilterContext(ActionExecutingContext filterContext)
    {

        string actionName = this.ControllerContext.RouteData.Values["action"].ToString();
        string controllerName = this.ControllerContext.RouteData.Values["controller"].ToString();

        if (actionName == "ReRouteToIndex")
            // Set up as a default 
            actionName = "Index";

        // Move to the same action and controller, but with the "lang" parameter set...
        var controller = (LocalizationController)filterContext.Controller;
        filterContext.Result = controller.RedirectToAction(actionName, controllerName, new { lang = strLang });
    }

Any help is much appreciated.

PatB
  • 11
  • 4
  • You don't need a `SelectLanguage` action, and you shouldn't be POSTing to change the language. See [ASP.NET MVC 5 culture in route and url](https://stackoverflow.com/a/32839796). – NightOwl888 Feb 23 '18 at 20:30
  • I see you are using RoutePrefix. Could it be that you are not filling it with the correct data ? Could you show us the Localization Controller? Maybe Some Route Data change and the Routing Module cannot make use of the attributed routes ? – Nick Polyderopoulos Feb 23 '18 at 20:33
  • @NightOwl888 - I should probably have explained that, the "SelectLanguage" does not change the site language but rather content in different languages (i.e. not part of the localization). – PatB Feb 24 '18 at 07:09
  • @Nick - Good idea, I will be back about this. – PatB Feb 24 '18 at 07:09
  • @PatB The RoutePrefix is must always be there for the route to work. So basically now you are doing the second part of you localization controller(being that your lang is always null with the url you are giving to us.). Try to fill up the lang parameter on your ActionLink and on your post form. – Nick Polyderopoulos Feb 24 '18 at 07:47
  • @Nick - Thanks for the idea. I'm using a package called "RouteLocalization.MVC" which should fill that part in from the user culture. I guess I have to look into that, maybe there is some issue with the package, even though it worked up until about a week ago... – PatB Feb 24 '18 at 08:07
  • @PatB you said you are using azure. I would check the application settings too. like framework running etc. What i like to do when debugging these kinds of things is to create a logger just for that. So Logging the route table data in your case and using the logging stream of the Azure you could see whats wrong with the app at the time that returns the 404. – Nick Polyderopoulos Feb 24 '18 at 09:12

1 Answers1

0

I answer this question in case anyone searches and finds the answer here.

It seems this behavior was caused by incoming links from Google AdWords pointing to http:// instead of https://. When trying to use the Html.ActionLink from the "incorrect" http:// the above error occurred. After fixing the incoming links to point to https:// as intended, the sudden and intermittent errors disappeared and all seems to have been ok the last 24hrs.

Thanks to @Nick and @Nightowl888 for support.

PatB
  • 11
  • 4