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);
}
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 }
.