Failed to fetch error with PUT request, MyBatis Mapper method not executing properly

Question:

I’m encountering an issue where a PUT request is failing with a Failed to fetch error, and the corresponding MyBatis Mapper method is not executing properly. Here’s a breakdown of the problem and what I’ve tried so far:

Problem Description

When I attempt to update a user profile using a PUT request, the server receives the request, but it remains in a pending state indefinitely. The server logs show that the method handling the request is called, but the MyBatis Mapper method does not seem to execute as expected. The database is not updated, and no error messages are being logged.

Code Snippets

JavaScript Code:

document.addEventListener('DOMContentLoaded', function () {
  const csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
  const csrfHeader = 'X-CSRF-TOKEN';

  const pfpContainer = document.getElementsByClassName("pfp-container")[0];
  const soloProfilePicture = document.getElementById("solo-pfp");
  const pfpInput = document.getElementById("solo-pfp-file-input");
  const saveBtn = document.getElementById("save-profile-update-btn");

  pfpContainer.addEventListener("click", function () {
    pfpInput.click();
  });

  pfpInput.addEventListener("change", function (changeEvent) {
    const file = changeEvent.target.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = function (loadEvent) {
        soloProfilePicture.src = loadEvent.target.result;
      };
      reader.readAsDataURL(file);
    }
  });

  saveBtn.onclick = function (event) {
    console.log("click");
    const data = {
      profileImageUrl: document.getElementById("solo-pfp").src,
      email: document.getElementById("email").innerText
    };

    fetch('/api/user/solo/update-profile', {
      method: 'PUT',
      headers: {
        [csrfHeader]: csrfToken,
        'Content-Type': 'application/json; charset=utf-8'
      },
      body: JSON.stringify(data)
    })
    .then(response => response.json())
    .then(resp => {
      alert("User Info Edit complete");
      window.location.href = "/";
    })
    .catch(error => {
      alert(JSON.stringify(error));
    });
  });
});

Spring Boot Controller Code:

@PutMapping({"/solo/update-profile"})
public ResponseEntity<?> updateProfileProcess(@RequestBody UserResponseDTO user,
    HttpSession session) {
  log.info("UserResponseDTO 생성 성공");
  log.info("1. updateProfileProcess 호출됨");
  log.info("2. Received user data: {}", user);
  try {
    userService.updateProfileImageUrl(user);
    log.info("3. updateProfileImageUrl 호출됨");
  } catch (Exception e) {
    log.error("Error updating profile image URL: ", e);
    return ResponseEntity.badRequest().body("Error updating profile: " + e.getMessage());
  }

  Authentication auth = SecurityContextHolder.getContext().getAuthentication();
  log.info("4. Authentication 객체 생성됨");
  if (auth != null && auth.isAuthenticated()) {
    log.info("5. 세션이 NULL이 아니고 로그인이 되어있음");
    session.setAttribute("profileImageUrl", user.getProfileImageUrl());
    log.info("6. 세션에 정보 삽입함");
  }
  return ResponseEntity.ok()
      .body(Map.of("message", "Profile updated successfully", "redirectUrl", "/user/solo/"));
}

MyBatis Mapper XML Code:

<mapper namespace="com.spring.moji.mapper.UserMapper">
  <resultMap id="userMap" type="com.spring.moji.entity.UserEntity">
    <id column="email" property="email"/>
    <result column="user_name" property="userName"/>
    <result column="birthday" property="birthday"/>
    <result column="gender" property="gender"/>
    <result column="password" property="password"/>
    <result column="created_at" property="createdAt"/>
    <result column="profile_image_url" property="profileImageUrl"/>
    <result column="couple_id" property="coupleId"/>
    <collection property="authList" resultMap="authMap"/>
  </resultMap>
  <resultMap id="authMap" type="com.spring.moji.entity.UserAuthEntity">
    <id column="auth_no" property="authNo"/>
    <result column="user_email" property="userEmail"/>
    <result column="auth" property="auth"/>
  </resultMap>

  <select id="login" resultMap="userMap">
    SELECT u.email, u.user_name, birthday, gender, password, profile_image_url, auth, couple_id
    FROM users u
    JOIN user_auth auth ON u.email = auth.user_email
    WHERE u.email = #{email}
  </select>

  <insert id="join">
    INSERT INTO users (email, user_name, birthday, gender, password, created_at)
    VALUES (#{email}, #{userName}, #{birthday}, #{gender}, #{password}, sysdate)
  </insert>

  <insert id="insertAuth">
    INSERT INTO user_auth (auth_no, user_email, auth)
    VALUES (user_auth_seq.nextval, #{userEmail}, #{auth})
  </insert>

  <update id="updateProfileImageUrl" parameterType="com.spring.moji.entity.UserEntity">
    UPDATE users
    SET profile_image_url = #{profileImageUrl}
    WHERE email = #{email}
  </update>
</mapper>

Troubleshooting Steps Taken

  1. Server Logs: Checked the server logs but did not find any specific errors or warnings related to the request processing.

  2. JavaScript Fetch API: Verified that the fetch request is properly configured with the correct CSRF token and headers.

  3. Network Activity: Monitored network activity and confirmed that the request is sent but remains in a pending state.

  4. Database Check: Ensured that the database connection and the updateProfileImageUrl query are properly configured.

Questions

  1. Why might the PUT request remain in a pending state and not complete?
  2. What could be causing the MyBatis Mapper method not to execute or update the database?
  3. Are there any common issues or misconfigurations in the provided code snippets that could lead to this problem?

I would appreciate any guidance or suggestions to resolve this issue. Thank you!