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

使用Fetch将复杂对象从JS传递到MVC控制器失败求助

解决ASP.NET MVC控制器Fetch API参数绑定失败问题

问题场景

现有ASP.NET MVC控制器方法,使用jQuery Ajax调用正常,但改用Fetch API时,控制器始终获取到id=0,Position对象属性全为null。

控制器代码

[HttpPost]
public async Task<IActionResult> GetTextFromPos(int id,Position position)
{
    if (position==null || _context.Batches == null)
    {
        return NotFound();
    }

    var batchesModel = await _context.Batches
        .Include(b => b.Profile)
        .ThenInclude(b => b.Fields)
        .FirstOrDefaultAsync(m => m.Id == id);

    batchesModel.OcrData = await _ocrStorageService.LoadOcrDataFromFileSystem(batchesModel.Id);

    var foundText = "This is found text: "+batchesModel.GetTextFromPosition(position);

    return Json(foundText); 
}

正常工作的jQuery Ajax代码

async function getTextFromPos(id, left, top, right, bottom) {
    const reverseScaleFactor = 1 / scale_factor;
    const reverseLeft = Math.round(left * reverseScaleFactor);
    const reverseTop = Math.round(top * reverseScaleFactor);
    const reverseRight = Math.round(right * reverseScaleFactor);
    const reverseBottom = Math.round(bottom * reverseScaleFactor);

    var payload = {
        id: id,
        position: { left: reverseLeft, top: reverseTop, right: reverseRight, bottom: reverseBottom }
    };
    $.ajax({
        url: '/Batches/GetTextFromPos',
        type: 'POST',
        data: payload,
        success: function (response) {
            console.log('Response: ', response);
        },
        error: function (error) {
            console.error('Error:', error);
        }
    });
}

绑定失败的Fetch API代码

async function getTextFromPos(id, left, top, right, bottom) {
    const reverseScaleFactor = 1 / scale_factor;
    const reverseLeft = Math.round(left * reverseScaleFactor);
    const reverseTop = Math.round(top * reverseScaleFactor);
    const reverseRight = Math.round(right * reverseScaleFactor);
    const reverseBottom = Math.round(bottom * reverseScaleFactor);

    const payload = {
        id: id,
        left: reverseLeft,
        top: reverseTop,
        right: reverseRight,
        bottom: reverseBottom
    };

    try {
        const response = await fetch('/Batches/GetTextFromPos', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(payload)
        });

        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }

        const responseData = await response.json();
        console.log('Response:', responseData);
    } catch (error) {
        console.error('Error:', error);
    }
}

问题原因

  1. Payload结构不匹配:jQuery的payload是嵌套的position对象,而Fetch的payload是平级的left/top/right/bottom属性,和控制器期望的参数结构不一致。
  2. ASP.NET MVC绑定规则限制:默认情况下,ASP.NET MVC只能从请求Body中绑定一个复杂类型对象,无法同时绑定多个参数(int id和Position position),除非明确指定参数的来源。

解决方案

方案一:统一Payload结构并使用ViewModel接收

1. 修改Fetch的Payload结构

将平级属性改为嵌套的position对象,和jQuery保持一致:

async function getTextFromPos(id, left, top, right, bottom) {
    const reverseScaleFactor = 1 / scale_factor;
    const reverseLeft = Math.round(left * reverseScaleFactor);
    const reverseTop = Math.round(top * reverseScaleFactor);
    const reverseRight = Math.round(right * reverseScaleFactor);
    const reverseBottom = Math.round(bottom * reverseScaleFactor);

    const payload = {
        id: id,
        position: { left: reverseLeft, top: reverseTop, right: reverseRight, bottom: reverseBottom }
    };

    try {
        const response = await fetch('/Batches/GetTextFromPos', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(payload)
        });

        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }

        const responseData = await response.json();
        console.log('Response:', responseData);
    } catch (error) {
        console.error('Error:', error);
    }
}

2. 创建ViewModel并修改控制器

定义一个包含Id和Position的ViewModel:

public class GetTextFromPosRequest
{
    public int Id { get; set; }
    public Position Position { get; set; }
}

修改控制器方法,用[FromBody]标记ViewModel参数:

[HttpPost]
public async Task<IActionResult> GetTextFromPos([FromBody] GetTextFromPosRequest request)
{
    if (request.Position == null || _context.Batches == null)
    {
        return NotFound();
    }

    var batchesModel = await _context.Batches
        .Include(b => b.Profile)
        .ThenInclude(b => b.Fields)
        .FirstOrDefaultAsync(m => m.Id == request.Id);

    batchesModel.OcrData = await _ocrStorageService.LoadOcrDataFromFileSystem(batchesModel.Id);

    var foundText = "This is found text: " + batchesModel.GetTextFromPosition(request.Position);

    return Json(foundText); 
}

方案二:拆分参数来源(Id从URL传递,Position从Body传递)

1. 修改Fetch请求的URL和Payload

将id作为查询参数拼接到URL,Body只传递Position的属性:

async function getTextFromPos(id, left, top, right, bottom) {
    const reverseScaleFactor = 1 / scale_factor;
    const reverseLeft = Math.round(left * reverseScaleFactor);
    const reverseTop = Math.round(top * reverseScaleFactor);
    const reverseRight = Math.round(right * reverseScaleFactor);
    const reverseBottom = Math.round(bottom * reverseScaleFactor);

    const payload = { left: reverseLeft, top: reverseTop, right: reverseRight, bottom: reverseBottom };

    try {
        const response = await fetch(`/Batches/GetTextFromPos?id=${id}`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(payload)
        });

        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }

        const responseData = await response.json();
        console.log('Response:', responseData);
    } catch (error) {
        console.error('Error:', error);
    }
}

2. 修改控制器参数标记

给Position参数添加[FromBody],明确从Body绑定:

[HttpPost]
public async Task<IActionResult> GetTextFromPos(int id, [FromBody] Position position)
{
    if (position == null || _context.Batches == null)
    {
        return NotFound();
    }

    var batchesModel = await _context.Batches
        .Include(b => b.Profile)
        .ThenInclude(b => b.Fields)
        .FirstOrDefaultAsync(m => m.Id == id);

    batchesModel.OcrData = await _ocrStorageService.LoadOcrDataFromFileSystem(batchesModel.Id);

    var foundText = "This is found text: " + batchesModel.GetTextFromPosition(position);

    return Json(foundText); 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 21:30:28