Why does request.getParameter return null when form data is submitted via JavaScript to a servlet?

I have the following JSP form code:

<div class="col-lg-7">
    <form action="FormServlet" method="post" id="contactForm" data-aos="fade-up" data-aos-delay="200">
        <div class="row gy-4">
            <div class="col-md-6">
                <label for="nameInput" class="pb-2">Your Name</label>
                <input type="text" name="userName" id="nameInput" class="form-control">
            </div>
            <div class="col-md-6">
                <label for="emailInput" class="pb-2">Your Email</label>
                <input type="email" class="form-control" name="userEmail" id="emailInput">
            </div>
            <div class="col-md-12">
                <label for="subjectInput" class="pb-2">Subject</label>
                <input type="text" class="form-control" name="messageSubject" id="subjectInput">
            </div>
            <div class="col-md-12">
                <label for="messageInput" class="pb-2">Message</label>
                <textarea class="form-control" name="userMessage" rows="10" id="messageInput"></textarea>
            </div>
            <div class="col-md-12 text-center">
                <div class="loading">Loading</div>
                <div class="errorMessage"></div>
                <div class="sentMessage">Your message has been sent. Thank you!</div>
                <button type="submit">Send Message</button>
            </div>
        </div>
    </form>
</div>

And this is the related JavaScript code included in my JSP:

document.getElementById('contactForm').addEventListener('submit', function (event) {
    event.preventDefault(); // Stop the form from submitting by default

    const form = this; // Reference the form
    const formData = new FormData(form); // Create a FormData object

    const actionUrl = form.action; // Action URL for form submission

    fetch(actionUrl, {
            method: 'POST',
            body: formData,
        })
        .then(response => response.ok)
        .then(result => {
            console.log(result); // Process the response
            form.querySelector('.sentMessage').classList.add('d-block');
        })
        .catch(error => {
            console.error('Submission error:', error);
        });
});

The corresponding servlet code is as follows:

package com.example.controller;

import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class ContactServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String firstName = request.getParameter("userName");
        System.out.println(firstName);

        if (firstName == null || firstName.isEmpty()) {
            response.getWriter().println("Name is required.");
            return;
        }
        PrintWriter out = response.getWriter();
        out.println("OK");
    }
}

When I remove the JavaScript, the form submits properly to the servlet and I can retrieve the name using request.getParameter("userName");. However, when I include the JavaScript, the console prints null instead.

When using JavaScript to submit a form, particularly through the Fetch API, there are a few common pitfalls that can lead to request.getParameter() returning null in your servlet. Based on your code and given that removing the JavaScript allows the form to work as expected, here’s a fresh perspective on potential problems and solutions:

  • Ensure Input Element Names are Correct: Double-check that each input’s name attribute matches the expected parameter in the servlet. Your JSP input elements look correctly named, so this might not be the issue, but it’s always good to verify.

  • Check the Form Submission Method: Although you mentioned using method: 'POST' in your Fetch request, ensure that both your form and Fetch call specify the same HTTP method.

  • Debugging FormData: As suggested, you can log the FormData in your JavaScript to ensure that all fields are correctly included:

for (let [key, value] of formData.entries()) {
    console.log(key, value); // Check if all expected fields and values are present
}
  • HTTP Headers in Fetch: Adding headers can sometimes be necessary if the server expects them, especially concerning Content-Type:
fetch(actionUrl, {
    method: 'POST',
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded' // Although FormData is being used, servers might expect this
    },
    body: new URLSearchParams(formData).toString(), // Convert to URL-encoded string
})
.then(response => response.ok)
.then(result => {
    console.log(result);
    form.querySelector('.sentMessage').classList.add('d-block');
})
.catch(error => {
    console.error('Submission error:', error);
});

By using URLSearchParams instead of directly passing formData, you might resolve discrepancies in how form data is processed server-side, leading to correct parameter retrieval.

  • Servlet Mapping: Make sure your servlet’s URL pattern is correctly mapped in web.xml or via annotations, matching the action attribute.

This approach should help verify and address any mismatches between what your JavaScript sends and what your servlet expects, ensuring no null values upon submission. If you’re still facing issues, review your server logs for any additional errors or warnings that might provide further insight.

You need to ensure your servlet URL matches in both the HTML form’s action attribute and JavaScript fetch request. Also, check if your form inputs have correct names in FormData. Try logging formData to verify:

for (let [key, value] of formData.entries()) {
    console.log(key, value);
}

This might expose any mapping issues between input names and servlet parameters. Make sure the method is POST in both the form and fetch.