Search code examples
javascriptasp.netasp.net-mvcasp.net-coremodel-view-controller

Passing complex objects from javascript to MVC controller using fetch


I have a controller method:

        [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); 

    }

I need to call this method from JS script. I had no problem using ajax:

async function getTextFromPos(id, left, top, right, bottom) {
    const reverseScaleFactor = 1 / scale_factor;
    // Reverse the scaling on the coordinates and round them to integers
    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) {
            // Handle the successful response here
            console.log('Response: ', response);
        },
        error: function (error) {
            // Handle any errors that occur during the request
            console.error('Error:', error);
        }
    });
    }

but when I tried to use fetch instead of ajax it doesnt work

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);
        // Handle the successful response here
    } catch (error) {
        console.error('Error:', error);
        // Handle any errors that occur during the request
    }
}

I tried changing content type, adding [FormBody] to parameters of controller function - nothing worked. Id is always 0 and position values are null.


Solution

  • I copied your relevant code, you can add relevant configuration in headers, for example: 'Accept': 'application/json', headers — Specify Accept and Content-Type HTTP request headers. Both headers are set to application/json to specify the received and sent media types respectively. And you can add frombody attribute in controller to accept data from request body.

    If you want to pass an additional id parameter, you want to use:

    public async Task<IActionResult> GetTextFromPos( int Id ,[FromBody] Position position) 
    

    this form receives the data, can insert the value of the JavaScript variable Id. You can refer to this example:

    My fetch :

    document.addEventListener("DOMContentLoaded", function () {
    
        const scale_factor = 1;
        const Id = 10;
        async function getTextFromPos(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 = {
                left: reverseLeft,
                top: reverseTop,
                right: reverseRight,
                bottom: reverseBottom
            }
    
            const response = await fetch(`/Fetch/GetTextFromPos/${Id}`, {
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(payload)
            })
                .then(response => response.json())
                .then(responseData => {
                    console.log('Success:', responseData);
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }
    
    
        const myLeft = 10;
        const myTop = 20;
        const myRight = 30;
        const myBottom = 40;
    
        getTextFromPos(myLeft, myTop, myRight, myBottom);
    });
    

    My controller:

    [HttpPost]
    public async Task<IActionResult> GetTextFromPos( int Id ,[FromBody] Position position)
    {
    var foundText = "This is found text: ";
    
    return Json(foundText);
    }
    

    When I send data by fetch:

    enter image description here enter image description here If you want to pass data in the form of the following code:

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

    You can refer to these two methods:

    First:

    You can create a new model to define the corresponding model, and then use the [FromBody] attribute to bind the related data to the model:

    New model:

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

    Fetch:

    document.addEventListener("DOMContentLoaded", function () {
    
         const scale_factor = 1;
         const Id = 10;
    
         async function getTextFromPos(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 }
             };
    
             try {
                 const response = await fetch('/Fetch/GetTextFromPos', {
                     method: 'POST',
                     headers: {
                         'Accept': 'application/json',
                         'Content-Type': 'application/json'
                     },
                     body: JSON.stringify(payload)
                 });
    
                 if (response.ok) {
                     const responseData = await response.json();
                     console.log('Success:', responseData);
                 } else {
                     console.error('Error:', response.statusText);
                 }
             } catch (error) {
                 console.error('Error:', error);
             }
         }
    
         const myLeft = 10;
         const myTop = 20;
         const myRight = 30;
         const myBottom = 40;
    
         getTextFromPos(myLeft, myTop, myRight, myBottom);
    });
    

    receive this model:

    enter image description here

    The second

    Use Jsondocument to receive it (in this way you need to parse the data and deserialize it): enter image description here