You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决视图返回空白页及HTTP 405方法不允许问题?

问题解决:HTTP 405错误及关注列表持久化失败

核心问题分析

  1. 405方法不允许错误:页面中「Add To Watchlist」使用的是<a>标签,默认发起GET请求,但后端Watchlist方法标记了[HttpPost],服务器拒绝GET请求,返回405。
  2. 类型转换错误:控制器中通过LINQ查询返回匿名类型,直接强转Stocks实体会抛出异常,导致代码中断,无法完成数据库插入。
  3. 敏感信息暴露风险:前端传递用户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>

额外优化建议

  1. 使用强类型ViewModel:替代ViewBag,创建WatchlistViewModel类,提升代码可读性和可维护性。
  2. 异常处理细化:捕获特定异常(如数据库异常),返回更友好的错误信息。
  3. 异步操作统一:所有数据库操作使用async/await,避免阻塞线程。

内容的提问来源于stack exchange,提问作者Tobzz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 20:40:17