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

在Razor Pages中如何通过jQuery调用C#后端接口?

解决方案:Razor Pages按需加载数据与JWT令牌处理

环境与需求回顾

  • 环境:ASP.NET Core 6.0、Razor Pages
  • 认证:JWT Bearer令牌存储在Session Storage
  • 核心需求:页面展示对象列表,每个对象的详情数据按需加载(不预查询),同时避免暴露原始数据

一、令牌获取的最优方式

不需要将Bearer令牌传递给分部视图,直接从前端Session Storage读取即可——令牌本就存储在前端,无需通过后端渲染传递,既减少冗余又避免令牌出现在HTML源码中。


二、两种按需加载方案(均避免原始数据暴露)

方案1:调用授权API端点(推荐,前后端分离式处理)

后端创建授权API控制器,在控制器内处理原始数据,只返回安全的展示字段,前端通过AJAX携带令牌请求。

1. 后端API控制器实现
[Authorize]
[ApiController]
[Route("api/parts")]
public class PartsApiController : ControllerBase
{
    private readonly IPartDataService _dataService;

    public PartsApiController(IPartDataService dataService)
    {
        _dataService = dataService;
    }

    [HttpGet("{partId}/details")]
    public async Task<IActionResult> GetPartDetails(int partId)
    {
        // 查询原始数据
        var rawData = await _dataService.GetPartInnerDataAsync(partId);
        // 仅返回需要展示的字段,过滤敏感/原始数据
        var safeViewModel = new 
        {
            DisplayName = rawData.FullName,
            Status = rawData.Status.ToString(),
            UpdateTime = rawData.LastUpdated.ToString("yyyy-MM-dd HH:mm")
        };
        return Ok(safeViewModel);
    }
}
2. 前端分部视图JS修改
@model dynamic

<div class="card">
    <div class="card-header">
        <button type="button" class="btn" data-bs-toggle="collapse" 
                data-bs-target="#partContent_@Model.PartData.partId"
                data-id="@Model.PartData.partId" onclick="queryInnerData(this);">
            @Model.PartData.DisplayName
        </button>
    </div>
    <div class="collapse" id="partContent_@Model.PartData.partId">
        <div class="card card-body">
            <div id="innerData_@Model.PartData.partId"></div>
        </div>
    </div>
</div>

<script type="text/javascript">
    async function queryInnerData(sender) {
        const partId = sender.getAttribute('data-id');
        const targetDiv = document.getElementById(`innerData_${partId}`);
        
        // 避免重复加载
        if (targetDiv.innerHTML.trim() !== '') return;

        // 从Session Storage取令牌(替换成你的令牌存储key)
        const token = sessionStorage.getItem('auth_token');
        if (!token) {
            targetDiv.innerHTML = '<p class="text-danger">请重新登录</p>';
            return;
        }

        try {
            const response = await fetch(`/api/parts/${partId}/details`, {
                method: 'GET',
                headers: {
                    'Authorization': `Bearer ${token}`,
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) throw new Error('数据加载失败');
            
            const data = await response.json();
            // 渲染安全数据
            targetDiv.innerHTML = `
                <p><strong>状态:</strong>${data.Status}</p>
                <p><strong>更新时间:</strong>${data.UpdateTime}</p>
            `;
        } catch (error) {
            targetDiv.innerHTML = `<p class="text-danger">${error.message}</p>`;
        }
    }
</script>

方案2:异步加载Razor分部视图(贴合Razor Pages生态)

通过请求Razor Page的处理方法,直接返回渲染好的HTML片段,后端完全控制输出内容,前端看不到任何原始数据。

1. 后端PageModel添加处理方法
public class IndexModel : PageModel
{
    private readonly IPartDataService _dataService;
    public List<PartModel> Parts;
    public string Id;

    public IndexModel(IPartDataService dataService)
    {
        _dataService = dataService;
    }

    public async Task<IActionResult> OnGetAsync(string Id)
    {
        // 加载初始列表数据
        Parts = await _dataService.GetPartsAsync(Id);
        return Page();
    }

    [Authorize]
    public async Task<IActionResult> OnGetPartDetailsPartial(int partId)
    {
        // 查询并处理数据,返回安全的视图模型
        var rawData = await _dataService.GetPartInnerDataAsync(partId);
        var viewModel = new PartDetailsViewModel
        {
            Status = rawData.Status.ToString(),
            UpdateTime = rawData.LastUpdated.ToString("yyyy-MM-dd HH:mm")
        };
        // 返回渲染好的分部视图
        return Partial("_PartDetailsPartial", viewModel);
    }
}
2. 创建分部视图_PartDetailsPartial.cshtml
@model PartDetailsViewModel

<p><strong>状态:</strong>@Model.Status</p>
<p><strong>更新时间:</strong>@Model.UpdateTime</p>
3. 前端JS修改(需携带AntiForgery令牌)
async function queryInnerData(sender) {
    const partId = sender.getAttribute('data-id');
    const targetDiv = document.getElementById(`innerData_${partId}`);
    
    if (targetDiv.innerHTML.trim() !== '') return;

    const token = sessionStorage.getItem('auth_token');
    if (!token) {
        targetDiv.innerHTML = '<p class="text-danger">请重新登录</p>';
        return;
    }

    try {
        const response = await fetch(`?handler=PartDetailsPartial&partId=${partId}`, {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${token}`,
                // Razor Pages必须携带AntiForgery令牌
                'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
            }
        });

        if (!response.ok) throw new Error('数据加载失败');
        
        const html = await response.text();
        targetDiv.innerHTML = html;
    } catch (error) {
        targetDiv.innerHTML = `<p class="text-danger">${error.message}</p>`;
    }
}

注意:确保页面包含@Html.AntiForgeryToken()(通常布局页已默认添加)。


核心优化总结

  1. 令牌安全:直接从Session Storage读取,避免后端传递导致的冗余与暴露风险
  2. 数据安全:后端统一处理原始数据,仅返回需要展示的内容或渲染好的HTML
  3. 性能优化:添加重复加载判断,避免无效请求
  4. 用户体验:完善错误处理,提供清晰的状态提示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 02:01:31