基于数据库实现URL重定向时遭遇Too Many Redirects错误排查
问题背景
实现基于数据库的URL重定向功能,redirectfrom字段存储源URL,redirectto字段存储目标URL,规则已成功存入数据库,但运行时触发"too many redirects"错误,相关代码如下:
RedirectUrl方法代码
public void RedirectUrl(string sourceUrl) { using (DrWaelGhanemEntities context = new DrWaelGhanemEntities()) { RedirectUrlsTable redirectRule = context.RedirectUrlsTables .FirstOrDefault(rule => rule.RedirectFrom == sourceUrl); if (redirectRule != null) { string destinationUrl = redirectRule.RedirectTo; // Check if the destination URL is the same as the source URL. if (string.Equals(sourceUrl, destinationUrl, StringComparison.OrdinalIgnoreCase)) { // Handle this case to prevent a redirection loop. HttpContext.Current.Response.StatusCode = 404; HttpContext.Current.Response.End(); } else { HttpContext.Current.Response.Redirect(destinationUrl, endResponse: true); } } else { // No matching rule found; you can handle this case as needed. HttpContext.Current.Response.StatusCode = 404; HttpContext.Current.Response.End(); } } }
Global.asax代码
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); } protected void Application_BeginRequest(object sender, EventArgs e) { UriBuilder builder = new UriBuilder(Request.Url); bool found = false; string URL = String.Empty; if (!HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("cpanel") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains(".axd") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("localhost") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("platform")) { if (!builder.Host.Contains("www")) { builder.Host = "www." + builder.Host; found = true; } URL = builder.ToString(); URL = Server.UrlDecode(URL); } if (URL.EndsWith("/") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("cpanel") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains(".axd") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("localhost") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("platform")) { found = true; URL = URL + "/"; } if (found && !Request.Url.IsLoopback) { Response.RedirectPermanent(URL, true); } }
问题排查
1. 斜杠添加逻辑导致死循环
Application_BeginRequest中的第二个判断存在逻辑错误:当URL已经以/结尾时,代码会再次添加一个/,形成xxx.com//的URL。服务器会自动将//解析为/,触发相同逻辑再次添加斜杠,最终导致循环重定向。
2. WWW跳转与数据库规则冲突
如果数据库中存在example.com/page → www.example.com/page的规则,同时Application_BeginRequest强制将非WWW URL跳转为WWW版本,会导致请求反复触发重定向逻辑;反之,若目标URL是非WWW而WWW跳转强制转WWW,也会引发循环。
3. URL匹配不标准化
RedirectUrl中使用精确字符串匹配rule.RedirectFrom == sourceUrl,但如果传入的sourceUrl与数据库存储的URL在大小写、WWW前缀、斜杠结尾上不一致,可能导致错误匹配,触发重复重定向。
解决方案
1. 修复斜杠处理逻辑
修改Application_BeginRequest中的斜杠判断,仅当URL不以/结尾时添加斜杠:
// 替换原斜杠判断代码 if (!URL.EndsWith("/") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("cpanel") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains(".axd") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("localhost") && !HttpContext.Current.Request.Url.AbsoluteUri.ToLower().Contains("platform")) { found = true; URL = URL + "/"; }
2. 统一URL格式,避免规则冲突
- 存储重定向规则时,统一URL格式:要么全带WWW,要么全不带,同时统一斜杠结尾规则。
- 调整
Application_BeginRequest执行顺序:先完成WWW和斜杠标准化,再调用RedirectUrl,确保传入的sourceUrl是标准化后的格式。
3. 优化RedirectUrl匹配逻辑
添加URL标准化辅助方法,统一匹配规则,避免大小写、前缀、斜杠差异导致的错误:
public void RedirectUrl(string sourceUrl) { // 标准化源URL string normalizedSource = NormalizeUrl(sourceUrl); using (DrWaelGhanemEntities context = new DrWaelGhanemEntities()) { RedirectUrlsTable redirectRule = context.RedirectUrlsTables .FirstOrDefault(rule => NormalizeUrl(rule.RedirectFrom) == normalizedSource); if (redirectRule != null) { string destinationUrl = redirectRule.RedirectTo; string normalizedDest = NormalizeUrl(destinationUrl); // 检测直接循环 if (normalizedSource == normalizedDest) { HttpContext.Current.Response.StatusCode = 404; HttpContext.Current.ApplicationInstance.CompleteRequest(); } else { // 使用永久重定向更利于SEO HttpContext.Current.Response.RedirectPermanent(destinationUrl, endResponse: false); HttpContext.Current.ApplicationInstance.CompleteRequest(); } } else { HttpContext.Current.Response.StatusCode = 404; HttpContext.Current.ApplicationInstance.CompleteRequest(); } } } // URL标准化辅助方法 private string NormalizeUrl(string url) { if (string.IsNullOrEmpty(url)) return url; Uri uri; if (!Uri.TryCreate(url, UriKind.Absolute, out uri)) return url; // 统一WWW前缀(按需调整为去掉WWW) string host = uri.Host.StartsWith("www.") ? uri.Host : $"www.{uri.Host}"; // 统一路径结尾斜杠 string path = uri.AbsolutePath.EndsWith("/") ? uri.AbsolutePath : $"{uri.AbsolutePath}/"; return new UriBuilder(uri.Scheme, host, uri.Port, path, uri.Query).ToString().ToLower(); }
4. 替换Response.End()避免请求终止问题
Response.End()会强制终止请求,可能引发异常,建议使用HttpContext.Current.ApplicationInstance.CompleteRequest()替代,同时保留状态码设置。
总结
核心问题来自Application_BeginRequest的错误斜杠逻辑,以及URL格式不统一导致的重定向冲突。通过修复斜杠处理、标准化URL格式、优化匹配逻辑,即可解决"too many redirects"错误。
内容的提问来源于stack exchange,提问作者Ahmed Alaa

