UPDATE: I changed the form a little bit and added some code to try and get the script to sync with the form. I also checked the payload and saw that only 2 out of 4 fields are being submitted to the form, which is probably what is causing the bad request.
I am trying to make a full stack ASP.NET Core application using Razor pages and am trying to make a form submit data properly.
Here is my view page with the form:
@page
@model RogersPizza.Pages.OrderModel
@{
Layout = "Shared/_Layout.cshtml";
}
@section title {
<title>Order</title>
}
@section scripts {
}
@section siteMenu {
<div name="Site Menu">
<table width="100%">
<tr>
<th><a href="Index">Rogers Pizza</a></th>
<th><a href="Menu">Menu</a></th>
<th>Order</th>
<th><a href="About">About</a></th>
<th><a href="Contact">Contact</a></th>
<th><a href="Employees">Employees</a></th>
</tr>
</table>
</div>
}
@section body {
<div name="Order UI">
<form method="post">
<p>Choose your pizza</p>
<select asp-for="Order.Pizza" id="pizzas" required>
@foreach(var item in Model?.Pizzas)
{
<!-- <option>@Html.DisplayFor(modelItem => item.Name)</option> -->
<option value="@item.Name">@item.Name</option>
}
</select>
<div id= "payment-options"></div>
<script src="~/js/paymentOptionsBundled.js"></script>
<input type="hidden" asp-for="Order.PaymentOption" id="selectedPaymentOption" />
<input type="hidden" asp-for="Order.GiftCardNumber" id="giftCardNumber" value="9999999999999999"/>
<input type="hidden" asp-for="Order.ID" name="Order.ID" value="999">
<input type="submit" value="Place Order" />
</form>
</div>
}
Here is the react script inside the form:
import React from 'react';
import { useState, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
export default function PaymentOptions() {
const [paymentOption, setPaymentOption] = useState("cash");
const [giftCard, setGiftCard] = useState("");
useEffect(() => {
const form = document.querySelector('form');
const syncHiddenInputs = () => {
const paymentInput = document.getElementById('selectedPaymentOption');
const giftCardInput = document.getElementById('giftCardNumber');
if (paymentInput) {
paymentInput.value = paymentOption;
}
if (giftCardInput) {
// if(paymentOption === "cash")
// {
// giftCardInput.value = "9999999999999999";
// }
// else if(paymentOption === "giftCard")
// {
// giftCardInput.value = giftCard || "";
// }
giftCardInput.value = paymentOption === "cash" ? "9999999999999999" : giftCard;
}
};
form?.addEventListener('submit', syncHiddenInputs);
return () => form?.removeEventListener('submit', syncHiddenInputs);
}, [paymentOption, giftCard]);
const handlePaymentMethodChange = (e) => {
setPaymentOption(e.target.value);
};
const handleGiftCardChange = (e) => {
setGiftCard(e.target.value);
};
return (
<div>
<div name="selectPaymentMethod">
<p>Please select a payment method</p>
</div>
<select name="paymentOptions" value={paymentOption} onChange={handlePaymentMethodChange}>
<option value="cash">Cash</option>
<option value="giftCard">Gift Card</option>
</select>
{paymentOption === "cash" &&
<div name="cash">
<p>Please have your cash ready when receiving your order.</p>
</div>
}
{paymentOption === "giftCard" &&
<div name="giftCard">
<p>Please enter your gift card number below.</p>
<input
type="text"
id="giftCardInput"
inputmode="numeric"
placeholder="Enter gift card number"
min="0"
minlength="16"
maxlength="16"
pattern="[0-9]{16}"
value={giftCard}
onChange={handleGiftCardChange}
required
/>
</div>
}
</div>
);
}
const domNode = document.getElementById('payment-options');
if (domNode) {
const root = createRoot(domNode);
root.render(<PaymentOptions />);
}
Here is the model page:
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RogersPizza.Models;
namespace RogersPizza.Pages
{
public class OrderModel : PageModel
{
private readonly RogersPizza.Data.StoreContext _context;
private readonly ILogger<OrderModel> _logger;
[BindProperty] public Order? Order { get; set; }
public OrderModel(RogersPizza.Data.StoreContext context, ILogger<OrderModel> logger)
{
_context = context;
_logger = logger;
}
public IList<Pizza>? Pizzas { get; set; }
public async Task OnGetAsync()
{
Pizzas = await _context.Pizzas.ToListAsync();
}
public IActionResult OnPost()
{
_logger.LogInformation("Received order: Pizza={Pizza}, Payment={PaymentOption}, GiftCard={GiftCardNumber}",
Order?.Pizza, Order?.PaymentOption, Order?.GiftCardNumber);
return RedirectToPage("/Index");
}
// private bool ValidateOrder(Order order)
// {
// return false;
// }
}
}
Here is the Order model used when binding the form:
namespace RogersPizza.Models
{
public class Order
{
public int? ID { get; set; }
public required string Pizza { get; set; }
public required string PaymentOption { get; set; }
public string? GiftCardNumber { get; set; }
}
}
Currently, my form bonding fails when submitting the form on the view page and I get a 400 error message page when using Chrome with the following message in the console:
Failed to load resource: the server responded with a status of 400 ()
Here is the payload. Notice how only 2 out of the 4 form fields are included. Pizza and GiftCardNumber should also be included:
paymentOptions=giftCard&Order.ID=999
How can I get my form to submit properly?
-
2Use your browser's dev-tools Network panel to inspect the request and response. ASP.NET is actually pretty good at providing details in the response body, it's far from silentPhil– Phil2025年08月05日 23:33:47 +00:00Commented Aug 5 at 23:33
-
2HTML input - name vs. idshingo– shingo2025年08月06日 06:05:59 +00:00Commented Aug 6 at 6:05
-
1I am not clear about what is making the request - is it razor page or react? In React code i don't see any form being used.Michał Turczyn– Michał Turczyn2025年08月06日 07:10:19 +00:00Commented Aug 6 at 7:10
-
@MichałTurczyn Razor is submitting the form. React is working with the form to only render certain fields depending on what payment method is selected.kevin29– kevin292025年08月07日 20:34:12 +00:00Commented Aug 7 at 20:34
-
1Citing the most crucial info: Name Attribute: Used on form elements to submit information. Only input tags with a name attribute are submitted to the servershingo– shingo2025年08月08日 06:30:33 +00:00Commented Aug 8 at 6:30
1 Answer 1
This is not a model binding issue. It's a Request Verification issue. Request Verification is a mechanism designed to prevent possible Cross Site Request Forgery attacks and is enabled by default in Razor Pages.
The most common cause of a 400 status code in Razor Pages is a failure to include the request verification token in a POST request initiated from client code, or where you doctor the payload using client code. You need to ensure that the hidden field __RequestVerificationToken
is included as part of the payload, either as a form value or a header. The hidden field should be generated by the form tag helper given that it has its method set to POST. If not, you can generate one in the page using @Html.AntiForgeryToken()
More information here: https://www.learnrazorpages.com/security/request-verification
1 Comment
name
attribute to all form fields instead of an asp-for
attribute helped to include all the fields in the request payload, I still had the 400 error after that change. After enabling more logging in my Program.cs file, I found logs in my terminal saying the required antiforgery cookie was not present.Explore related questions
See similar questions with these tags.