I have an ASP.NET Core Web API endpoint that receives all null values when called from jQuery/browser, but works perfectly when called from Postman with the exact same JSON payload.
The Problem
When I send a POST request from my front-end (jQuery/TypeScript), all properties in my DTO arrive as null in the backend. However, when I send the exact same JSON from Postman, all values are received correctly.
Backend Controller
[HttpPost("create-book")]
public ObjectResult CreateBook([FromBody] CreateBookDto createBookDto)
{
try
{
var book = createBookDto.ToBook();
var createdBook = _servicesFactory.BookService.CreateBook(book);
return Ok(new BookDto().FromBook(createdBook));
}
catch (Exception ex)
{
return Fail<BookDto>(ex, "Error creating book");
}
}
Frontend Code (TypeScript/jQuery)
public createBook(bookData: any, callback: Function) {
const json = typeof bookData === 'string'
? bookData
: JSON.stringify(bookData);
this.ajaxCall(
'api/book/create-book',
HttpMethods.POST,
json,
[callback],
ContentType.ApplicationJson,
null
);
}
What I've Verified
The JSON is valid and identical in both cases
Headers are identical
-same Content-Type -same session cookie etc.The response is 200 OK in both cases
Other POST endpoints in the same controller work fine from the browser (e.g., /api/book/property/update
I've added case insensitive option to Startup.cs - just in case. Same result..
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
EDIT: Here's my ajaxCall implementation
public ajaxCall(endpoint: string,
method: HttpMethods,
content: any,
callbacks: Array<Function>, contentType) {
var self = this;
$.ajax({
method: HttpMethods[method],
url: this._contextPath + endpoint,
contentType: contentType === ContentType.ApplicationFormUrlEncoded ? "application/x-www-form-urlencoded; charset=UTF-8" : "application/json; charset=utf-8",
data: content
})
.done(function (apiResponse, textStatus, jqXHR) {
if(!apiResponse.success && apiResponse.metadata && apiResponse.metadata.errorId){
self._errorObserver.errorReceived(apiResponse.metadata.message+ ":" + apiResponse.metadata.errorId)
} else {
for (let callback of callbacks) {
if (callback) {
callback(apiResponse);
}
}
}
})
.fail(function (msg) {
console.log('ajax call http error: ' + msg.status + ' ' + msg.statusText);
if (msg.status === 401) {
document.location.href = self._contextPath;
} else if (msg.status === 0) {
// call aborted (page was changed?)
} else {
//console.log('ajax communication error: ' + msg.status + ' ' + msg.statusText);
}
}).always(function () {});
}
Here are the calls from web and postman
and the JSON looks like this:
{
"ArtNr":"09764000",
"OnlineArtNr":null,
"AutorHrsg":"Ballreich",
"Titel":"Fallkommentar zum UmwandlungsR",
"AuflageNr":7,
"LegalRegulId":null,
"HerstellerId":null,
"BemerkProduktStufe":"Titel auf 2025 verschoben, Planung AE steht aus. HINWEIS: Autor schreibt bei 2 Werken mit | SCHWERDTFEGER geht vor |Info 25.01.2024: MS-Abgabe Schwerdtfeger erfolgt Mitte/Ende M..rz als h..ndische (!) Korrektur im Ausdruck (ca. 90 S.)",
"AktuelleDelays":0,
"CaDruckauflageVorabschaetzung":300,
"DruckauflageLVP":0,
"CaUmfangVorabschaetzung":500,
"UmfangIST":0,
"BemerkUebergMSChase":"MS-Bearbeitung und Kontaktaufnahme durch PE soll lt. AE erst nach Abschluss Projekt Schwerdtfeger erfolgen",
"DruckAnlManuDays":15,
"AuslieferungTermFix":null,
"Bruttopreis":null,
"Subspreis":null,
"SubspreisGueltBis":null,
"ProduktgruppeId":null,
"Bemerkung":null,
"ALAVerchicktAm":null,
"ReviewTermin":null,
"TemplateId":null,
"InhaltFarbeId":null,
"InhaltPapierId":null,
"LeinenmaterialId":null,
"PraegungId":null,
"PraegungFarbeId":null,
"Bemerkungen":null,
"AkAt":0,
"CE3":false,
"ContentFreeze":false,
"WorkInProgress":true,
"OutOfPlanning":false
}
EDIT 2: Here are the requests, as requested by comments (in raw format) Web:
curl 'http://localhost:8081/api/book/create-book' \
-H 'Accept: */*' \
-H 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json; charset=UTF-8' \
-b 'JSESSIONID=C4A0D7767F6C59F1C38D56BE1A9307AD' \
-H 'Origin: http://localhost:8081' \
-H 'Referer: http://localhost:8081/create' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: same-origin' \
-H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'sec-ch-ua: "Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
--data-raw $'{"ArtNr":"1621256","OnlineArtNr":null,"AutorHrsg":"Ballreich","Titel":"Fallkommentar zum UmwandlungsR","AuflageNr":7,"LegalRegulId":null,"HerstellerId":null,"BemerkProduktStufe":"Titel auf 2025 verschoben, Planung AE steht aus. HINWEIS: Autor schreibt bei 2 Werken mit | SCHWERDTFEGER geht vor |Info 25.01.2024: MS-Abgabe Schwerdtfeger erfolgt Mitte/Ende März als händische (\u0021) Korrektur im Ausdruck (ca. 90 S.)","AktuelleDelays":0,"CaDruckauflageVorabschaetzung":300,"DruckauflageLVP":0,"CaUmfangVorabschaetzung":500,"UmfangIST":0,"BemerkUebergMSChase":"MS-Bearbeitung und Kontaktaufnahme durch PE soll lt. AE erst nach Abschluss Projekt Schwerdtfeger erfolgen","DruckAnlManuDays":15,"AuslieferungTermFix":null,"Bruttopreis":null,"Subspreis":null,"SubspreisGueltBis":null,"ProduktgruppeId":null,"Bemerkung":null,"ALAVerchicktAm":null,"ReviewTermin":null,"TemplateId":null,"InhaltFarbeId":null,"InhaltPapierId":null,"LeinenmaterialId":null,"PraegungId":null,"PraegungFarbeId":null,"Bemerkungen":null,"AkAt":0,"CE3":false,"ContentFreeze":false,"WorkInProgress":true,"OutOfPlanning":false}'
POSTMAN:
curl --location 'http://localhost:5000/api/book/create-book' \
--header 'Content-Type: application/json' \
--header 'Authorization: ••••••' \
--data '{
"ArtNr":"09764000",
"OnlineArtNr":null,
"AutorHrsg":"Ballreich",
"Titel":"Fallkommentar zum UmwandlungsR",
"AuflageNr":7,
"LegalRegulId":null,
"HerstellerId":null,
"BemerkProduktStufe":"Titel auf 2025 verschoben, Planung AE steht aus. HINWEIS: Autor schreibt bei 2 Werken mit | SCHWERDTFEGER geht vor |Info 25.01.2024: MS-Abgabe Schwerdtfeger erfolgt Mitte/Ende M..rz als h..ndische (!) Korrektur im Ausdruck (ca. 90 S.)",
"AktuelleDelays":0,
"CaDruckauflageVorabschaetzung":300,
"DruckauflageLVP":0,
"CaUmfangVorabschaetzung":500,
"UmfangIST":0,
"BemerkUebergMSChase":"MS-Bearbeitung und Kontaktaufnahme durch PE soll lt. AE erst nach Abschluss Projekt Schwerdtfeger erfolgen",
"DruckAnlManuDays":15,
"AuslieferungTermFix":null,
"Bruttopreis":null,
"Subspreis":null,
"SubspreisGueltBis":null,
"ProduktgruppeId":null,
"Bemerkung":null,
"ALAVerchicktAm":null,
"ReviewTermin":null,
"TemplateId":null,
"InhaltFarbeId":null,
"InhaltPapierId":null,
"LeinenmaterialId":null,
"PraegungId":null,
"PraegungFarbeId":null,
"Bemerkungen":null,
"AkAt":0,
"CE3":false,
"ContentFreeze":false,
"WorkInProgress":true,
"OutOfPlanning":false
}'}'
A WORKING POST request for property/update (just as an example)
curl 'http://localhost:8081/api/book/property/update' \
-H 'Accept: */*' \
-H 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json; charset=UTF-8' \
-b 'JSESSIONID=C4A0D7767F6C59F1C38D56BE1A9307AD' \
-H 'Origin: http://localhost:8081' \
-H 'Referer: http://localhost:8081/p1' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: same-origin' \
-H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'sec-ch-ua: "Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
--data-raw '{"bookId":"1","property":"Book.BM","value":"118","propertyType":"Single"}'

ajaxCall? jQuery uses$.ajax(. If the payload can't be converted it means it's not the same. .NET doesn't care where the calls come from. It cares about the content type and the shape of the JSON payload. Right now we don't even know what the request or JSON look like, only your assertion they're the same, which is obviously not the case. At the very least post the POSTMAN request; charset=UTF-8to the Content-Type header in your AJAX request, whereas in postman you are setting onlyapplication/json- maybe the server doesn't handle the former correctly?application/jsonis always UTF8. I don't think that would prevent deserialization though. This does show that the calls are not the same