如何解决视图返回空白页及HTTP 405方法不允许问题?
问题解决:HTTP 405错误及关注列表持久化失败
核心问题分析
- 405方法不允许错误:页面中「Add To Watchlist」使用的是
<a>标签,默认发起GET请求,但后端Watchlist方法标记了[HttpPost],服务器拒绝GET请求,返回405。 - 类型转换错误:控制器中通过LINQ查询返回匿名类型,直接强转
Stocks实体会抛出异常,导致代码中断,无法完成数据库插入。 - 敏感信息暴露风险:前端传递用户ID到后端,即使使用POST,也不如直接在控制器从身份系统获取安全。
修复步骤及代码修改
1. 前端:将链接替换为POST表单(Index.cshtml)
把原来的<a>标签替换为隐藏表单,通过按钮触发POST请求,避免GET传递敏感信息:
<!-- 替换原来的Add To Watchlist链接 --> <form asp-controller="Stocks" asp-action="Watchlist" method="post" style="display:inline;"> <input type="hidden" name="stockTicker" value="@item.Ticker" /> <button type="submit" class="btn btn-link p-0">Add To Watchlist</button> </form>
注:
style="display:inline;"保持按钮和其他链接在同一行,避免布局错乱。
2. 后端:修复控制器逻辑(StocksController.cs)
- 移除
userId参数,直接通过UserManager获取当前登录用户ID,更安全。 - 修复LINQ查询,直接返回
Stocks实体,避免匿名类型转换错误。 - 添加重复添加的校验,防止同一用户重复关注同一股票。
修改后的代码:
[HttpPost] [ValidateAntiForgeryToken] // 防止CSRF攻击,需前端配合添加防伪令牌 public async Task<IActionResult> Watchlist(string stockTicker) { try { // 获取当前登录用户ID,无需前端传递 var user = await _userManager.GetUserAsync(User); if (user == null) { return RedirectToAction("Login", "Account"); // 未登录跳转登录页 } var userId = await _userManager.GetUserIdAsync(user); // 校验股票是否存在 var stock = await _context.Stocks.FirstOrDefaultAsync(s => s.Ticker == stockTicker); if (stock == null) { return View("Error", new ErrorViewModel { RequestId = Guid.NewGuid().ToString(), Description = "指定股票不存在" }); } // 校验是否已关注该股票,避免重复添加 var existingWatchlist = await _context.Watchlists.FirstOrDefaultAsync(w => w.GenericUserId == userId && w.Ticker == stockTicker); if (existingWatchlist != null) { // 已关注可跳转至关注列表或提示 return RedirectToAction("Watchlist"); } // 添加至关注列表 _context.Watchlists.Add(new Watchlist { GenericUserId = userId, Ticker = stock.Ticker, Price = stock.Price, Exchange = stock.Exchange }); await _context.SaveChangesAsync(); // 查询用户的关注列表 var watchlistStocks = await _context.Watchlists .Where(w => w.GenericUserId == userId) .Select(w => new { w.Ticker, w.Price, w.Exchange }) .ToListAsync(); ViewBag.Stocks = watchlistStocks; return View(); } catch (Exception ex) { return View("Error", new ErrorViewModel { RequestId = ex.ToString(), Description = $"错误信息:{ex.Message}" }); } } // 添加GET版本的Watchlist方法,用于展示关注列表 [HttpGet] public async Task<IActionResult> Watchlist() { var user = await _userManager.GetUserAsync(User); if (user == null) { return RedirectToAction("Login", "Account"); } var userId = await _userManager.GetUserIdAsync(user); var watchlistStocks = await _context.Watchlists .Where(w => w.GenericUserId == userId) .Select(w => new { w.Ticker, w.Price, w.Exchange }) .ToListAsync(); ViewBag.Stocks = watchlistStocks; return View(); }
注:需在前端表单中添加
@Html.AntiForgeryToken(),配合控制器的[ValidateAntiForgeryToken]使用。
3. 确保Watchlist视图存在
创建Watchlist.cshtml视图,用于展示用户的关注列表:
@{ ViewData["Title"] = "我的关注列表"; } <h1>我的关注列表</h1> @if (ViewBag.Stocks != null && ViewBag.Stocks.Count > 0) { <table class="table"> <thead> <tr> <th>股票代码</th> <th>价格</th> <th>交易所</th> </tr> </thead> <tbody> @foreach (var item in ViewBag.Stocks) { <tr> <td>@item.Ticker</td> <td>@item.Price</td> <td>@item.Exchange</td> </tr> } </tbody> </table> } else { <p>您还没有关注任何股票</p> } <p><a asp-action="Index">返回股票列表</a></p>
额外优化建议
- 使用强类型ViewModel:替代
ViewBag,创建WatchlistViewModel类,提升代码可读性和可维护性。 - 异常处理细化:捕获特定异常(如数据库异常),返回更友好的错误信息。
- 异步操作统一:所有数据库操作使用
async/await,避免阻塞线程。
内容的提问来源于stack exchange,提问作者Tobzz
相关产品推荐
相关产品推荐

