bezbos.
bezbos.

Reputation: 2364

ASP.NET MVC 5 - jQuery AJAX works, Fetch API doesn't

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. enter image description here

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

Answers (1)

mi105
mi105

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

Related Questions