Reputation: 2364
In my ASP.NET MVC 5 project I have setup local APIs using WebApi and I'm checking for user roles using ASP.NET Identity in all APIs that change data. When I make a POST request to api/createPerson
using jQuery AJAX it works fine. However when I use my own Fetch API library the server returns HTML of the login page instead of the JSON data.
As you can see JSON parsing fails(because it's HTML and not valid JSON code).
However, If I disable the block of code in the API controller:
if (!User.IsInRole("Admin"))
return Unauthorized();
The server returns JSON normally.
Why does my User.IsInRole("Admin")
check return true
when I'm using a jQuery AJAX request, yet it returns false
when I'm using Fetch API?
Code that works(jQuery AJAX):
$.ajax({
method: "POST",
url: `/api/createPerson/`,
type: "application/json",
data: {
id: idHidden,
firstName: firstNameTxt.val(),
lastName: lastNameTxt.val(),
phoneNumber: phoneTxt.val(),
gender: genderDropDown.val(),
birthDate: birthdateTxt.val(),
email: emailTxt.val(),
stateId: statesDropDown.val(),
cityId: citiesDropDown.val()
},
success: function (message) {
// ...
},
error: function (error) {
if (error.responseJSON.modelState)
showValidationMessages(error.responseJSON.modelState);
});
Code that doesn't work(Fetch API):
httpFetch.post("/api/createPerson/", {
id: idHidden,
firstName: firstNameTxt.val(),
lastName: lastNameTxt.val(),
phoneNumber: phoneTxt.val(),
gender: genderDropDown.val(),
birthDate: birthdateTxt.val(),
email: emailTxt.val(),
stateId: statesDropDown.val(),
cityId: citiesDropDown.val()
}).then(message => {
// ...
})
API Controller code:
[HttpPost]
[Route("api/createPerson")]
public IHttpActionResult CreatePerson(PersonDto personDto)
{
//Check for privileges(ADMIN ONLY!)
if (!User.IsInRole("Admin"))
return Unauthorized();
// Check if model state is valid
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Check if person already exists in the database, if true, update it
if (personDto.Id != 0)
{
// Try updating person in db
try
{
var personInDb = _context.Persons.SingleOrDefault(p => p.Id == personDto.Id);
personInDb.FirstName = personDto.FirstName;
personInDb.LastName = personDto.LastName;
personInDb.Gender = personDto.Gender;
personInDb.Email = personDto.Email;
personInDb.BirthDate = personDto.BirthDate;
personInDb.PhoneNumber = personDto.PhoneNumber;
personInDb.StateId = personDto.StateId;
personInDb.CityId = personDto.CityId;
_context.SaveChanges();
}
catch (Exception)
{
// if ERROR
return BadRequest(ModelState);
}
// if SUCCESS
return Ok($"{personDto.FirstName} {personDto.LastName} updated in the database!");
}
// If person doesn't exist in db, add new person to db
try
{
var person = new Person
{
Id = personDto.Id,
FirstName = personDto.FirstName,
LastName = personDto.LastName,
Gender = personDto.Gender,
Email = personDto.Email,
BirthDate = personDto.BirthDate,
PhoneNumber = personDto.PhoneNumber,
StateId = personDto.StateId,
CityId = personDto.CityId
};
_context.Persons.Add(person);
_context.SaveChanges();
}
catch (Exception)
{
// if ERROR
return BadRequest(ModelState);
}
// if SUCCESS
return Ok($"{personDto.FirstName} {personDto.LastName} added to the database!");
}
My Fetch API library snippet:
"use strict";
function httpHelper() {
this.post = async function (url, data) {
let response = await fetch(url, {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify(data)
});
let resData = await response.json();
return resData;
}
}
Upvotes: 1
Views: 5017
Reputation: 159
This issue is related with the following answer: JS Fetch API not working with ASP.NET Core 2 Controllers with Authorize attribute
The problem is that fetch is not sending authentication data by default. You have to explicitly state credentials: 'include' in fetch request.
As you are not sending the authorization data by default, the server returns the html shown to user when the user action is unauthorized. This html can not be deserialized into json.
Upvotes: 3