从 React 前端使用 API POST 到 Entity Framework 控制器时总是会出现空异常
Always get null exceptions when using API POST to an Entity Framework controller from a React front-end
我正在使用 .NetCore 3.1 Entity Framework 创建在线游戏系统。
我有一堆模型和控制器设置,每个模型在我的 MSSQL 数据库中代表一个 table,每个模型都有一个控制器。
这些模型的控制器工作正常。
但是现在,我有一个表单,用户可以在其中创建一个包含两个不同模型的新对象。
所以当用户提交表单时,需要在两者中创建一个新项目models/tables。
所以我创建了一个单独的模型 class 就像这样:
namespace My_Game.Models
{
public partial class CreateGame
{
public virtual StarSystem StarSystem { get; set; }
public virtual Ship Ship { get; set; }
}
}
以下是上述模型使用的两个模型:
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; }
}
这是我的控制器,它应该处理 API 调用:
[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);
}
这是用于将表单发送到 API 的前端 React 组件:
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 >
)
}
但是每当我尝试从反应页面点击控制器时,我只会在调试模式下收到此错误:
System.ArgumentNullException: 值不能为空。 (参数'entity')
我也尝试使用 Postman 进行测试,并将虚拟测试值放入主体中,但我得到了同样的错误。
我错过了什么?
谢谢!
最有可能的问题是发送数据的格式与操作中使用的模型不匹配。模型活页夹无法根据发送的内容进行填充。
发送表单数据后,更新操作以期望来自表单的数据
[HttpPost]
public async Task<ActionResult> ProcessForm([FromForm] CreateGame newGameEntry) {
//...
}
接下来更新客户端以发送适当的表单数据。
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 >
)
请注意名称更改以匹配所发送模型的结构。这样模型绑定器就会知道如何将发送的数据映射到预期的模型。
基于 React Forms 参考我下面的演示 post 表单数据到控制器:
import React, { Component } from 'react';
import axios from 'axios';
export class FormTest extends Component {
constructor(props) {
super(props);
this.state = {
starSystemName: "",
starSystemLocation:"",
shipName: "",
shipType: "",
shipMaxCrew:""
};
this.handleStarSystemName = this.handleStarSystemName.bind(this);
this.handleStarSystemLocation = this.handleStarSystemLocation.bind(this);
this.handleShipName = this.handleShipName.bind(this);
this.handleShipType = this.handleShipType.bind(this);
this.handleShipMaxCrew = this.handleShipMaxCrew.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleStarSystemName(event) {
this.setState({
starSystemName: event.target.value
});
}
handleStarSystemLocation(event) {
this.setState({
starSystemLocation: event.target.value
});
}
handleShipName(event) {
this.setState({
shipName: event.target.value
});
}
handleShipType(event) {
this.setState({
shipType: event.target.value
});
}
handleShipMaxCrew(event) {
this.setState({
shipMaxCrew: event.target.value
});
}
handleSubmit = (e) => {
e.preventDefault();
const data = new FormData();
data.append("starSystem.Name", this.state.starSystemName);
data.append("starSystem.Location", this.state.starSystemLocation);
data.append("ship.Name", this.state.shipName);
data.append("ship.Type", this.state.shipType);
data.append("ship.MaxCrew", this.state.shipMaxCrew);
axios({
method: 'post',
url: 'https://localhost:44301/WeatherForecast',
data: data,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
}
render() {
return (
<div id="newGameForm" >
<form id="myForm" onSubmit={this.handleSubmit}>
<input type="text" value={this.state.starSystemName} placeholder="Enter star system name:" onChange={this.handleStarSystemName} />
<input type="text" value={this.state.starSystemLocation} placeholder="Enter star system location:" onChange={this.handleStarSystemLocation} />
<input type="text" value={this.state.shipName} placeholder="Enter ship name:" onChange={this.handleShipName} />
<input type="text" value={this.state.shipType} placeholder="Enter ship type:" onChange={this.handleShipType} />
<input type="text" value={this.state.shipMaxCrew} placeholder="Enter max crew:" onChange={this.handleShipMaxCrew}/>
<button type="submit">Submit</button>
</form>
</div >
)
}
}
控制器:
[HttpPost]
public async Task<ActionResult> ProcessForm([FromForm] CreateGame newGameEntry)
我正在使用 .NetCore 3.1 Entity Framework 创建在线游戏系统。
我有一堆模型和控制器设置,每个模型在我的 MSSQL 数据库中代表一个 table,每个模型都有一个控制器。
这些模型的控制器工作正常。
但是现在,我有一个表单,用户可以在其中创建一个包含两个不同模型的新对象。
所以当用户提交表单时,需要在两者中创建一个新项目models/tables。
所以我创建了一个单独的模型 class 就像这样:
namespace My_Game.Models
{
public partial class CreateGame
{
public virtual StarSystem StarSystem { get; set; }
public virtual Ship Ship { get; set; }
}
}
以下是上述模型使用的两个模型:
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; }
}
这是我的控制器,它应该处理 API 调用:
[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);
}
这是用于将表单发送到 API 的前端 React 组件:
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 >
)
}
但是每当我尝试从反应页面点击控制器时,我只会在调试模式下收到此错误:
System.ArgumentNullException: 值不能为空。 (参数'entity')
我也尝试使用 Postman 进行测试,并将虚拟测试值放入主体中,但我得到了同样的错误。
我错过了什么?
谢谢!
最有可能的问题是发送数据的格式与操作中使用的模型不匹配。模型活页夹无法根据发送的内容进行填充。
发送表单数据后,更新操作以期望来自表单的数据
[HttpPost]
public async Task<ActionResult> ProcessForm([FromForm] CreateGame newGameEntry) {
//...
}
接下来更新客户端以发送适当的表单数据。
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 >
)
请注意名称更改以匹配所发送模型的结构。这样模型绑定器就会知道如何将发送的数据映射到预期的模型。
基于 React Forms 参考我下面的演示 post 表单数据到控制器:
import React, { Component } from 'react';
import axios from 'axios';
export class FormTest extends Component {
constructor(props) {
super(props);
this.state = {
starSystemName: "",
starSystemLocation:"",
shipName: "",
shipType: "",
shipMaxCrew:""
};
this.handleStarSystemName = this.handleStarSystemName.bind(this);
this.handleStarSystemLocation = this.handleStarSystemLocation.bind(this);
this.handleShipName = this.handleShipName.bind(this);
this.handleShipType = this.handleShipType.bind(this);
this.handleShipMaxCrew = this.handleShipMaxCrew.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleStarSystemName(event) {
this.setState({
starSystemName: event.target.value
});
}
handleStarSystemLocation(event) {
this.setState({
starSystemLocation: event.target.value
});
}
handleShipName(event) {
this.setState({
shipName: event.target.value
});
}
handleShipType(event) {
this.setState({
shipType: event.target.value
});
}
handleShipMaxCrew(event) {
this.setState({
shipMaxCrew: event.target.value
});
}
handleSubmit = (e) => {
e.preventDefault();
const data = new FormData();
data.append("starSystem.Name", this.state.starSystemName);
data.append("starSystem.Location", this.state.starSystemLocation);
data.append("ship.Name", this.state.shipName);
data.append("ship.Type", this.state.shipType);
data.append("ship.MaxCrew", this.state.shipMaxCrew);
axios({
method: 'post',
url: 'https://localhost:44301/WeatherForecast',
data: data,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
}
render() {
return (
<div id="newGameForm" >
<form id="myForm" onSubmit={this.handleSubmit}>
<input type="text" value={this.state.starSystemName} placeholder="Enter star system name:" onChange={this.handleStarSystemName} />
<input type="text" value={this.state.starSystemLocation} placeholder="Enter star system location:" onChange={this.handleStarSystemLocation} />
<input type="text" value={this.state.shipName} placeholder="Enter ship name:" onChange={this.handleShipName} />
<input type="text" value={this.state.shipType} placeholder="Enter ship type:" onChange={this.handleShipType} />
<input type="text" value={this.state.shipMaxCrew} placeholder="Enter max crew:" onChange={this.handleShipMaxCrew}/>
<button type="submit">Submit</button>
</form>
</div >
)
}
}
控制器:
[HttpPost]
public async Task<ActionResult> ProcessForm([FromForm] CreateGame newGameEntry)