Java Web & Servlets Fundamentals

Handling Requests

18 min Lesson 5 of 13

Handling Requests

Every meaningful servlet begins the same way: the container hands you an HttpServletRequest object and expects you to extract whatever data the client sent. That single object is the gateway to query-string parameters, form fields, HTTP headers, path information, cookies, and the raw request body. Understanding how to read from it accurately — and safely — is one of the most practical skills in server-side Java.

The HttpServletRequest Object

HttpServletRequest extends ServletRequest and gives you everything HTTP-specific. The container populates it before calling your doGet or doPost method, so it is fully ready to use as soon as you receive it. You never construct this object yourself.

The most commonly used groups of methods are:

  • Parameter access — query-string and form-body values.
  • Header access — HTTP headers sent by the browser or upstream proxy.
  • Request metadata — method, URI, context path, remote address.
  • Attribute store — a Map-like bag for passing objects between components during one request.

Reading Query-String and Form Parameters

Whether data arrives on the URL (?name=Alice&age=30) or in a form body with Content-Type: application/x-www-form-urlencoded, the servlet API presents it identically through getParameter. This uniformity is intentional: you describe what you need, not where it came from.

@WebServlet("/search") public class SearchServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Single value — returns null if the parameter is absent String query = req.getParameter("q"); String page = req.getParameter("page"); // Safe integer conversion with a sensible default int pageNum = 1; if (page != null && !page.isBlank()) { try { pageNum = Integer.parseInt(page.trim()); } catch (NumberFormatException e) { pageNum = 1; // treat garbage input as the first page } } resp.setContentType("text/plain;charset=UTF-8"); resp.getWriter().printf("Searching for: %s (page %d)%n", query, pageNum); } }
getParameter returns null, never an empty string, when a parameter is missing entirely. Always null-check before calling methods on the result. Skipping this is one of the most common sources of NullPointerException in servlet code.

Multi-Value Parameters

A checkbox group or a repeated URL parameter (e.g. ?tag=java&tag=web&tag=servlet) sends the same key multiple times. getParameter returns only the first value in that case. Use getParameterValues to retrieve all of them as a String[].

String[] tags = req.getParameterValues("tag"); if (tags != null) { for (String tag : tags) { // process each tag } }

If you want a full picture of every parameter name the client sent, getParameterMap() returns a Map<String, String[]> — each key maps to the complete array of values for that name. This is useful for logging, debugging, or building generic form processors.

Map<String, String[]> params = req.getParameterMap(); params.forEach((name, values) -> System.out.println(name + " = " + Arrays.toString(values)));
Parameter encoding: The container decodes percent-encoded characters (e.g. %20 → space) automatically. However, it uses the request's declared character encoding to do so. For non-ASCII form data, always call req.setCharacterEncoding("UTF-8") before the first call to getParameter — once the parameters are read, changing the encoding has no effect.

Reading HTTP Headers

HTTP headers carry metadata the client attaches to every request: the browser name, accepted content types, authentication tokens, cache directives, and more. You read them by name through getHeader(String name). Header names are case-insensitive by the HTTP specification, and the servlet API honours that.

String userAgent = req.getHeader("User-Agent"); String accept = req.getHeader("Accept"); String authHeader = req.getHeader("Authorization"); // e.g. "Bearer eyJ..." String contentType = req.getContentType(); // shortcut for Content-Type // Numeric headers — use getIntHeader to avoid manual parsing int contentLength = req.getIntHeader("Content-Length"); // -1 if absent // Date headers (returns milliseconds since epoch, or -1 if absent) long ifModifiedSince = req.getDateHeader("If-Modified-Since");

When you need to inspect every header the client sent, use getHeaderNames(), which returns an Enumeration<String>:

Enumeration<String> names = req.getHeaderNames(); while (names.hasMoreElements()) { String name = names.nextElement(); System.out.println(name + ": " + req.getHeader(name)); }
Never trust the User-Agent or X-Forwarded-For headers for security decisions. Both are trivially forged by any HTTP client. Use them only for analytics, logging, or content negotiation — never for authentication or access control.

Request Metadata

Beyond parameters and headers, HttpServletRequest exposes structural information about the request itself. These methods are essential when writing generic filters, logging middleware, or URL routing logic.

String method = req.getMethod(); // "GET", "POST", "PUT", … String requestURI = req.getRequestURI(); // "/app/search" String contextPath = req.getContextPath(); // "/app" (empty string for ROOT context) String servletPath = req.getServletPath(); // "/search" String queryString = req.getQueryString(); // "q=java&page=2" (null if none) String remoteAddr = req.getRemoteAddr(); // client IP (may be proxy IP) String scheme = req.getScheme(); // "http" or "https" int serverPort = req.getServerPort(); // 8080, 443, …

Reading the Raw Request Body

For POST or PUT requests carrying JSON, XML, or other non-form content types, the body arrives as a byte stream. You access it through getInputStream() (for binary) or getReader() (for text). The two are mutually exclusive: calling both in the same request throws an IllegalStateException.

// Reading a JSON body as a String if ("application/json".equals(req.getContentType())) { req.setCharacterEncoding("UTF-8"); StringBuilder sb = new StringBuilder(); try (BufferedReader reader = req.getReader()) { String line; while ((line = reader.readLine()) != null) { sb.append(line); } } String json = sb.toString(); // pass json to a JSON library (Gson, Jackson, etc.) }
Reading the body consumes it. Unlike parameters, which are cached internally, reading from the input stream or reader is a one-shot operation. Once read, the data is gone for that request. This matters when you write filters that inspect the body: you must buffer it and replace the stream, or use a wrapper.

Putting It Together: A Realistic Search Endpoint

Here is a servlet that combines everything above — parameters, headers, and metadata — into a pattern you will recognise from real applications:

@WebServlet("/api/products") public class ProductSearchServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); // Parameters String keyword = req.getParameter("keyword"); String category = req.getParameter("category"); String[] tags = req.getParameterValues("tag"); int page = parseIntOrDefault(req.getParameter("page"), 1); int size = parseIntOrDefault(req.getParameter("size"), 20); // Header — detect whether the client wants JSON String accept = req.getHeader("Accept"); boolean wantsJson = accept != null && accept.contains("application/json"); // Metadata String ip = req.getRemoteAddr(); // Log the request System.out.printf("[%s] GET /api/products keyword=%s page=%d wantsJson=%b%n", ip, keyword, page, wantsJson); // ... delegate to service layer, then write response resp.setContentType(wantsJson ? "application/json" : "text/html"); resp.getWriter().write("{}"); // placeholder } private int parseIntOrDefault(String value, int defaultValue) { if (value == null || value.isBlank()) return defaultValue; try { return Integer.parseInt(value.trim()); } catch (NumberFormatException e) { return defaultValue; } } }

Summary

HttpServletRequest is your single point of contact with everything the client sent. Use getParameter for single values and getParameterValues when a key can repeat. Always null-check and validate before parsing. Read headers with getHeader — and remember they are advisory, not authoritative, for security purposes. For non-form bodies, use getReader() or getInputStream(), being aware that reading is destructive. The next lesson covers HttpServletResponse: once you have read the request correctly, you will know exactly what to write back.