Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Always get null exceptions when using API POST to an Entity Framework controller from a React front-end

I am using .NetCore 3.1 Entity Framework to create an online gaming system.

I have a bunch of models and controllers setup and each model represents a table in my MSSQL database and each model has a controller.

The controllers for those models are working fine.

But now, I have a form where the user will create a new object consisting of two different models.

So when the user submits the form, it will need to create a new item in both models/tables.

So I created a separate model class just for that like this:

namespace My_Game.Models
{
    public partial class CreateGame
    {
        public virtual StarSystem StarSystem { get; set; }
        public virtual Ship Ship { get; set; }
    }
}

Here are the two models the above model uses:

public partial class StarSystem
{
    public string Name { get; set; }
    public long Location { get; set; }
}

public partial class Ship
{
    public string Name { get; set; }
    public string Type { get; set; }
    public long MaxCrew { get; set; }
}

And here is my controller that is supposed to handle the API call:

[HttpPost]
public async Task<ActionResult> ProcessForm([FromBody] CreateGame newGameEntry)
{
    // Read StarSystem data from form and add to DB via EF
    _context.StarSystem.Add(newGameEntry.StarSystem);
    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateException)
    {
        if (StarSystemExists(newGameEntry.StarSystem.Id))
        {
            return Conflict();
        }
        else
        {
            throw;
        }
    }

    // Read mentor data from form and add to DB via EF
    _context.Ship.Add(newGameEntry.Ship);
    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateException)
    {
        if (ShipExists(newGameEntry.Ship.Id))
        {
            return Conflict();
        }
        else
        {
            throw;
        }
    }

    return Ok();
}
private bool ShipExists(long id)
{
    return _context.Ship.Any(e => e.Id == id);
}

private bool StarSystemExists(long id)
{
    return _context.StarSystem.Any(e => e.Id == id);
}

Here is the frontend React component that is used to send the form to the API:

import React, { useState } from 'react';
import axios from 'axios';

const App = () => {

    const handleSubmit = (e) => {
        e.preventDefault()
        const { myForm } = e.target.elements.myForm
        axios.post('https://localhost:44376/api/formprocessor', { form: myForm })
    }

    return (
        <div id="newGameForm">
            <form id="myForm" onSubmit={handleSubmit}>
                <input type="text" name="starSystemName" placeholder="Enter star system name:" />
                <input type="text" name="starSystemLocation" placeholder="Enter star system location:" />
                <input type="text" name="shipName" placeholder="Enter ship name:" />
                <input type="text" name="shipType" placeholder="Enter ship type:" />
                <input type="text" name="shipMaxCrew" placeholder="Enter max crew:" />                  
                <button type="submit">Submit</button>
            </form>
        </div >
    )
}

But whenever I try to hit the controller from the react page, I just get this error when in debug mode:

System.ArgumentNullException: Value cannot be null. (Parameter 'entity')

I also tried testing with Postman, and put dummy test values in the body and I get the same error.

What am I missing?

Thanks!

like image 641
SkyeBoniwell Avatar asked Mar 10 '26 11:03

SkyeBoniwell


1 Answers

The most likely problem is that the format of the sent data does not match to the model used in the action. The model binder is unable to populate based on what is being sent.

Since sending form data, update the action to expect data from a form

[HttpPost]
public async Task<ActionResult> ProcessForm([FromForm] CreateGame newGameEntry) {
    //...
}

Next update the client side to send the appropriate form data.

const qs = require('querystring')

//...

const handleSubmit = (e) => {
    e.preventDefault()
    const { myForm } = e.target.elements.myForm
    const form = new FormData();
    for ( const key in myForm) {
        form.append(key, myForm[key]);
    }

    axios({
        method: 'post',
        url: 'https://localhost:44376/api/formprocessor',
        data: qs.stringify(form),
        headers: {'Content-Type': 'application/x-www-form-urlencoded' }
    });
}

return (
    <div id="newGameForm">
        <form id="myForm" onSubmit={handleSubmit}>
            <input type="text" name="starSystem.Name" placeholder="Enter star system name:" />
            <input type="text" name="starSystem.Location" placeholder="Enter star system location:" />
            <input type="text" name="ship.Name" placeholder="Enter ship name:" />
            <input type="text" name="ship.Type" placeholder="Enter ship type:" />
            <input type="text" name="ship.MaxCrew" placeholder="Enter max crew:" />
            <button type="submit">Submit</button>
        </form>
    </div >
)

Note the name change to match the structure of the models being sent. That way the model binder will know how to map the sent data to the intended models.

Reference Model Binding in ASP.NET Core

like image 132
Nkosi Avatar answered Mar 13 '26 01:03

Nkosi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!