Java Web & Servlets Fundamentals

Handling Forms: GET vs POST

18 min Lesson 7 of 13

Handling Forms: GET vs POST

Forms are the primary way users send data to a server. Whether it is a login box, a search field, or a multi-step checkout, every HTML form eventually delivers its data as an HTTP request. This lesson covers exactly how that mechanism works, why the choice between GET and POST matters, and how your servlet reads, validates, and safely uses submitted field values.

How HTML Forms Send Data

An HTML form has two critical attributes: method and action. The action is the URL that receives the submission; the method is either get or post. When the user clicks Submit, the browser packages all named input fields into an HTTP request and sends it to that URL.

<!-- A registration form targeting /app/register --> <form method="post" action="${pageContext.request.contextPath}/register"> <label>Username: <input type="text" name="username" required /> </label> <label>Email: <input type="email" name="email" required /> </label> <label>Password: <input type="password" name="password" required /> </label> <button type="submit">Register</button> </form>

Each <input name="..."> becomes one request parameter. The name attribute is the key; the field value is the value. Multiple inputs with the same name produce multiple values for that key — exactly what checkboxes and multi-selects use.

GET vs POST: the Real Distinction

Both methods deliver parameters, but they differ in placement, visibility, and semantics.

  • GET appends parameters to the URL as a query string: /search?q=servlet&page=2. The data is visible in the address bar, stored in browser history, bookmarkable, and can be cached. GET requests are idempotent — repeating them has no side effect.
  • POST places parameters in the HTTP request body, invisible in the URL. No bookmarking, no caching by default. POST requests are meant to change state — creating a record, placing an order, logging in.
The semantic rule: Use GET for queries and navigation (search, filter, paginate). Use POST for mutations (create, update, delete, authenticate). This is not just style — browsers re-submit POST forms with a confirmation dialog when you press Back or Refresh, precisely because POST is understood to have side effects.

doGet and doPost in Your Servlet

The servlet container calls doGet for GET requests and doPost for POST requests. Override whichever methods your form uses. A servlet that handles a search form (GET) and a submission form (POST) at the same URL overrides both:

import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @WebServlet("/register") public class RegisterServlet extends HttpServlet { /** Render the empty form */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/WEB-INF/views/register.jsp").forward(req, resp); } /** Process the submitted form */ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); String email = req.getParameter("email"); String password = req.getParameter("password"); // validate, persist, then redirect if (username == null || username.isBlank()) { req.setAttribute("error", "Username is required."); req.getRequestDispatcher("/WEB-INF/views/register.jsp").forward(req, resp); return; } // ... save to DB ... // PRG pattern: redirect after successful POST resp.sendRedirect(req.getContextPath() + "/dashboard"); } }

Reading Request Parameters

The HttpServletRequest API gives you several methods to access submitted data:

  • getParameter(name) — returns the first (or only) value for a field name as a String, or null if the parameter was not sent at all.
  • getParameterValues(name) — returns a String[] with all values for that name (checkboxes, multi-selects).
  • getParameterMap() — returns an immutable Map<String, String[]> of every parameter in the request, useful for iteration and logging.
// Single value String city = req.getParameter("city"); // Multiple values from a checkbox group String[] interests = req.getParameterValues("interests"); if (interests != null) { for (String interest : interests) { System.out.println("Interest: " + interest); } } // All parameters (for logging/debugging) req.getParameterMap().forEach((key, values) -> System.out.println(key + " = " + String.join(", ", values)) );
Never trust raw input. getParameter() returns exactly what the client sent. Always validate length, format, and range before using a value. Always HTML-escape user-provided strings before writing them back into a response — a direct echo of unescaped input is a Cross-Site Scripting (XSS) vulnerability.

Handling Numeric and Optional Fields

Every parameter arrives as a String. Conversion is your responsibility, and it can throw:

String ageParam = req.getParameter("age"); int age; try { age = Integer.parseInt(ageParam); // NumberFormatException if blank or non-numeric if (age < 0 || age > 150) throw new IllegalArgumentException("Age out of range"); } catch (NumberFormatException | IllegalArgumentException e) { req.setAttribute("error", "Please enter a valid age."); req.getRequestDispatcher("/WEB-INF/views/form.jsp").forward(req, resp); return; }

The Post-Redirect-Get (PRG) Pattern

After a successful POST, always redirect — do not forward to a success page. If you forward, the browser's URL still points at the POST endpoint. When the user refreshes, the browser resubmits the form, which can create duplicate records or repeat a payment. A redirect changes the URL in the browser to a safe GET, so refresh simply reloads the result page.

// At the end of a successful doPost: resp.sendRedirect(req.getContextPath() + "/orders/confirmation?id=" + newOrderId);
PRG is not optional in production. Every e-commerce site, every login flow, every form that writes data should use it. You will also see it called the "redirect after post" pattern. Implement it by default; only skip it when you have a deliberate reason (e.g., a file-upload progress page that must stay on the same URL).

Character Encoding

If your form contains non-ASCII characters (Arabic, Chinese, accented letters) you must tell the container which encoding the request body uses, before you call getParameter for the first time:

@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); // must be first line before any getParameter resp.setContentType("text/html; charset=UTF-8"); String name = req.getParameter("name"); // now correctly decoded // ... }

In a real application, set encoding once in a servlet filter rather than repeating it in every doPost. Modern containers (Tomcat 10+, Jakarta EE 10) use UTF-8 by default, so this step may already be handled for you — but verify rather than assume.

Summary

HTML forms submit data as HTTP parameters. Use GET for safe, idempotent operations and POST for state-changing ones. Override doGet and doPost in your servlet to handle each method. Read fields with getParameter and getParameterValues; always validate and sanitize before use. End every successful POST with a redirect (PRG pattern) to prevent duplicate submissions. Set UTF-8 encoding before reading parameters when your form accepts international input.