Search code examples
asp.net-mvchttp-redirect

RedirectToAction redirects to the same URL resulting in infinite redirection loop


I want to make a virtual folder browser for myself (not of a file system) and I am using optional route syntax to make good-looking bookmarkable URLs. However, it is possible to pass nulls in the middle of the route, and in that case (or in the case if the folder does not exist), I would like to simply redirect to the nearest valid folder.

But when I pass Folder/1st/2nd/%20/4th, and my custom function tries to redirect to Folder/1st/2nd, the framework redirects to the same previous URL, thus getting the browser stuck in an infinite redirection loop.

I have tried RedirectToActionPermanent but nada. return Redirect("/Folder/1st/2nd"); works, and changing any of the routeValues manually also makes it work. Otherwise the same URL is returned.

This is the generated response:

HTTP/1.1 302 Found
Content-Length: 0
Date: Tue, 24 Dec 2024 08:01:07 GMT
Server: Kestrel
Location: /Folder/1st/2nd/%20/4th

Endpoint:

[Route("Folder/{folder1?}/{folder2?}/{folder3?}/{folder4?}/{folder5?}")]
public IActionResult Index(string? folder1 = null, string? folder2 = null, string? folder3 = null, string? folder4 = null, string? folder5 = null)
{
    var path = ParsePath(folder1, folder2, folder3, folder4, folder5);

    if (path.Count == 0)
    {
        return View("Root");
    }
    else if (path.Any(string.IsNullOrWhiteSpace))
    {
        var validPath = GetValidPath(path);
        return RedirectToPath(validPath);
    }

    return new JsonResult(new { path });
}

Helper functions:

private List<string?> ParsePath(string? folder1, string? folder2, string? folder3, string? folder4, string? folder5)
{
    var result = new List<string?>(5)
    {
        folder1, folder2, folder3, folder4, folder5
    };

    for (var i = result.Count - 1; i >= 0; i--)
    {
        var folder = result[i];

        if (string.IsNullOrWhiteSpace(folder))
        {
            result.RemoveAt(i);
        }
        else
        {
            break;
        }
    }

    return result;
}

private List<string> GetValidPath(List<string?> path)
{
    var result = new List<string>(path.Count);

    foreach (var folder in path)
    {
        if (string.IsNullOrWhiteSpace(folder))
        {
            break;
        }
        else
        {
            result.Add(folder);
        }
    }

    return result;
}

private IActionResult RedirectToPath(List<string> path)
{
    var routeValues = new RouteValueDictionary();

    for (var i = 0; i < path.Count; i++)
    {
        var folderKey = $"folder{i + 1}";
        var folderValue = path[i];
        routeValues.Add(folderKey, folderValue);
    }

    return RedirectToAction(nameof(Index), routeValues);
}

Solution

  • This behavior is not only present for RedirectToAction, but also to links generated using Url.Action.

    While I cannot find out why it behaves like that, a workaround that I came up with is to continue generating links as is, but to include the next route segment as null:

    So if we want to generate a URL with route values new { folder1 = "1st", folder2 = "2nd" }, we also add folder3 = (string?)null.

    If we want to target the root, we pass new { folder1 = (string?)null }.