Azure App Service访问视图报ERR_TOO_MANY_REDIRECTS,本地VS运行正常
问题排查与解决方案
问题背景
使用Visual Studio 2022 + ASP.NET Core 6 + jQuery v3.6.0开发的应用,本地运行正常,但部署到Azure App Service后访问UserList视图时出现ERR_TOO_MANY_REDIRECTS错误,已清除Cookie但无效。
相关代码如下:
导航链接
<li class="nav-item"> <a class="dropdown-item" asp-area="" asp-controller="UserList" asp-action="UserList">User List</a> </li>
控制器代码
public async Task<IActionResult> UserList() { List<UserListViewModel> users = await _userListControllerHelper.MapToViewModel(_context); return View(users); }
MapToViewModel方法代码
public async Task<List<UserListViewModel>> MapToViewModel(ApplicationDbContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); List<UserListViewModel> model = new List<UserListViewModel>(); foreach (var user in context.Users) { var userDetails = new UserListViewModel(); userDetails.Username = user.UserName ?? string.Empty; var roles = await _userManager.GetRolesAsync(user); foreach (var role in roles) { userDetails.Role += role; } userDetails.Email = user.Email ?? string.Empty; IEnumerable<UsersHospital> usersHospitals = _usersHosptialRepository.GetHospitalIdsFromUserId(user.Id); userDetails.UsersHospitalId = usersHospitals is not null ? usersHospitals.FirstOrDefault().id : 0; userDetails.Hospital = usersHospitals is not null ? _lookupRepository.EnabledHospitals().FirstOrDefault(x => x.HospitalId == usersHospitals.FirstOrDefault().HospitalId).Hospital : string.Empty; userDetails.HasEmailBeenConfirmed = user.EmailConfirmed; userDetails.IsLockedOut = user.LockoutEnabled && (user.LockoutEnd is not null && user.LockoutEnd > DateTime.Now); model.Add(userDetails); } return model.ToList(); }
视图代码
@model IEnumerable<UserListViewModel> @section breadcrumb{ <nav aria-label="breadcrumb"> <ol class="breadcrumb"> <li class="breadcrumb-item active" aria-current="page">User List</li> </ol> </nav> } @if (TempData["AlertMessage"] != null) { <div class="alert alert-message alert-success"> @TempData["alertMessage"] </div> } <div class="row"> <div class="card col-md-12 border-0"> <div class="card-body align-content-center"> <a class="btn btn-primary btn-block" asp-area="Identity" asp-page="/Account/Register">Register New User</a> </div> </div> </div> <div class="row mb-5"> <div class="card-deck col-md-12 overflow-auto"> <div class="card col-md-12 border p-0"> <div class="card-header"> <b>List of Active Users</b> </div> <div class="UsersList card-body align-content-center"> <div class="row p-1 border-1 border-bottom border-top flex-container"> <div class="col-xxl-2 col-xl-2 col-lg-2 col-md-2 border-1 border-end"><b>Username</b></div> <div class="col-xxl-2 col-xl-2 col-lg-2 col-md-2 border-1 border-end"><b>User Email</b></div> <div class="col-xxl-1 col-xl-1 col-lg-1 col-md-1 border-1 border-end"><b>User Email Confirmed</b></div> <div class="col-xxl-2 col-xl-2 col-lg-2 col-md-2 border-1 border-end"><b>User Role</b></div> <div class="col-xxl-2 col-xl-2 col-lg-2 col-md-2 border-1 border-end"><b>User Location</b></div> <div class="col-xxl-3 col-xl-3 col-lg-3 col-md-3"><b>Actions</b></div> </div> @if (Model != null) { @foreach (var item in Model) { @if (!item.IsLockedOut) { <div class="row p-1 border-1 border-bottom flex-container card-deck"> <div class="card col-xxl-2 col-xl-2 col-lg-2 col-md-2 col-sm-2 border-1 border-end">@item.Username</div> <div class="card col-xxl-2 col-xl-2 col-lg-2 col-md-2 col-sm-2 border-1 border-end">@item.Email</div> <div class="card col-xxl-1 col-xl-1 col-lg-1 col-md-1 border-1 border-end"> @item.HasEmailBeenConfirmed @if (!item.HasEmailBeenConfirmed) { <br /> <a asp-controller="UserList" asp-action="ResendConfirmationEmail" class="btn btn-primary btn-block" asp-route-useremail="@item.Email" asp-route-returnUrl="@Url.Action("UserList", "UserList", new { area = "" })"> Resend </a> } </div> <div class="card col-xxl-2 col-xl-2 col-lg-2 col-md-2 border-1 border-end">@Html.DisplayFor(modelItem => item.Role)</div> <div class="card col-xxl-2 col-xl-2 col-lg-2 col-md-2 border-1 border-end">@Html.DisplayFor(modelItem => item.Hospital)</div> <div class = "col-xxl-3 col-xl-3 col-lg-3 col-md-3"> <a asp-controller="UserList" method="get" asp-action="UserEdit" asp-route-userEmail="@item.Email" class="btn btn-primary btn-block"> Edit User </a> <button class="lockUnlockOrDeleteUser btn btn-primary btn-block" data-id="@item.Email" data-type="lock"> Lock User </button> <button class="lockUnlockOrDeleteUser btn btn-danger btn-block" data-id="@item.Email" data-type="delete"> Delete User </button> </div> </div> } } } </div> </div> </div> </div> <div class="row"> <div class="card-deck col-md-12 overflow-auto"> <div class="card col-md-12 border p-0"> <div class="card-header"> <b>List of Locked Out Users</b> </div> <div class="UsersList card-body align-content-center"> <div class="row p-1 border-1 border-bottom border-top flex-container"> <div class="col-xxl col-xl col-lg col-md border-1 border-end"><b>Username</b></div> <div class="col-xxl col-xl col-lg col-md border-1 border-end"><b>User Role</b></div> <div class="col-xxl col-xl col-lg col-md border-1 border-end"><b>User Email</b></div> <div class="col-xxl col-xl col-lg col-md border-1 border-end"><b>User Location</b></div> <div class="col-xxl-3 col-xl-3 col-lg-3 col-md-3"><b>Actions</b></div> </div> @if (Model != null) { @foreach (var item in Model) { @if (item.IsLockedOut) { <div class="row p-1 border-1 border-bottom flex-container"> <div class="col-xxl col-xl col-lg col-md border-1 border-end">@item.Username</div> <div class="col-xxl col-xl col-lg col-md border-1 border-end">@item.Role</div> <div class="col-xxl col-xl col-lg col-md border-1 border-end">@item.Email</div> <div class="col-xxl col-xl col-lg col-md border-1 border-end">@item.Hospital</div> <div class="col-xxl-3 col-xl-3 col-lg-3 col-md-3"> <button class="lockUnlockOrDeleteUser btn btn-primary btn-block" data-id="@item.Email" data-type="unlock"> Unlock User </button> <button class="lockUnlockOrDeleteUser btn btn-danger btn-block" data-id="@item.Email" data-type="delete"> Delete User </button> </div> </div> } } } </div> </div> </div> </div> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} <script> $(document).on('click', '.lockUnlockOrDeleteUser', (event) => { event.preventDefault(); if (confirm("Are you sure you want to " + event.target.dataset.type + " this user?: " + event.target.dataset.id)) { $.ajax({ url: '@Url.Action("LockUnlockOrDeleteUser", "UserList")', type: 'POST', data: { userEmail: event.target.dataset.id, type: event.target.dataset.type }, }) .done(function (response) { switch (response.succeeded) { case false: alert("Failed to " + event.target.dataset.type + " user, please contact FNSD administration for help."); break; case true: alert("User successfully " + event.target.dataset.type + "ed"); var urlToRedirect = '@Url.Action("userlist", "userlist")'; window.location.href = urlToRedirect; break; default: alert(JSON.stringify(response) + ": Please contact FNSD administration for help."); } }) .fail(function (jqXHR, textStatus, errorThrown) { alert("AJAX request failed: " + errorThrown + ": Please contact FNSD administration for help."); }); }; }); </script> }
排查方向与解决方案
1. 身份验证/授权配置冲突
- 检查Azure App Service的身份验证设置:若启用了App Service Authentication(Easy Auth),可能和应用内的ASP.NET Core身份验证规则冲突,导致循环重定向。可暂时关闭Easy Auth测试,若问题解决则需调整两者配置,确保权限规则一致。
- 验证
[Authorize]特性配置:确认当前用户拥有访问UserList的权限,若权限不足,应用可能持续重定向到登录页,形成循环。需检查Azure环境中用户角色是否正确同步。
2. HTTPS重定向规则问题
- 检查
Program.cs中的HTTPS重定向代码:
Azure App Service默认处理HTTPS,但应用内重定向规则可能和Azure配置冲突。可在Azure门户应用服务 > 配置 > 常规设置中开启HTTPS强制,同时调整应用内配置:app.UseHttpsRedirection();app.UseHttpsRedirection(new HttpsRedirectionOptions { RedirectStatusCode = StatusCodes.Status307TemporaryRedirect });
3. 路由与URL大小写问题
- 修正视图中AJAX回调的重定向URL:Azure环境区分大小写,将
@Url.Action("userlist", "userlist")改为@Url.Action("UserList", "UserList"),和控制器/Action名称保持一致。 - 检查
Program.cs中的路由规则:确认无重复路由指向UserList Action,避免路由解析导致的重定向循环。
4. 代码潜在异常未处理
- 修复
MapToViewModel中的空值引用风险:
未处理的异常可能被全局中间件重定向,引发循环。var firstHospital = usersHospitals?.FirstOrDefault(); userDetails.UsersHospitalId = firstHospital != null ? firstHospital.id : 0; userDetails.Hospital = firstHospital != null ? _lookupRepository.EnabledHospitals().FirstOrDefault(x => x.HospitalId == firstHospital.HospitalId)?.Hospital ?? string.Empty : string.Empty;
5. 缓存与Cookie配置
- 关闭Azure App Service的ARR缓存:在门户应用服务 > 配置 > 缓存中关闭缓存测试。
- 检查身份验证Cookie的域配置:确保Cookie域设置适配Azure环境,避免因Cookie无法传递导致的持续登录重定向。
内容的提问来源于stack exchange,提问作者KevBarri
相关产品推荐
相关产品推荐

