HttpServlet.service() examines the request method and dispatches standard HTTP requests to doGet(), doPost(), doPut(), doPatch(), doDelete(), doHead(), doOptions(), or doTrace(). Jakarta Servlet 6.1 added a dedicated doPatch() method, so current applications do not need to override service() merely to support PATCH.
A handler name is only the entry point. Correct design also depends on HTTP semantics: whether the operation is safe, whether repeating it should have the same intended effect, which status describes the result, and whether a response body is appropriate.
Override only the methods the resource supports. The default HttpServlet implementation returns an error for unsupported methods, while doOptions can report supported methods through the Allow header. GET should retrieve a representation without causing accountable side effects; HEAD returns the corresponding headers without a body.
| Handler | Intent | Safe | Idempotent |
|---|---|---|---|
| doGet() | Read a representation | Yes | Yes |
| doPost() | Create or execute a command | No | Usually no |
| doPut() | Replace resource state at a known URI | No | Yes |
| doPatch() | Apply a partial modification | No | Not guaranteed |
| doDelete() | Remove a resource | No | Yes in intended effect |
| doHead() | Read headers corresponding to GET | Yes | Yes |
package com.example;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.WebServlet;
import java.io.*;
@WebServlet("/form")
public class FormServlet extends HttpServlet {
// GET: Show the form
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<!DOCTYPE html><html><body>");
out.println("<h2>Registration Form</h2>");
out.println("<form method='post' action='/form'>");
out.println(" Name: <input type='text' name='name'/><br/>");
out.println(" Email: <input type='email' name='email'/><br/>");
out.println(" <button type='submit'>Register</button>");
out.println("</form></body></html>");
}
// POST: Process the form
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String name = req.getParameter("name");
String email = req.getParameter("email");
// Validate
if (name == null || name.trim().isEmpty()) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Name is required");
return;
}
// Process (save to DB, etc.)
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<h2>Registration Successful!</h2>");
out.println("<p>Name: " + name + "</p>");
out.println("<p>Email: " + email + "</p>");
}
}
Return 200 when a representation is included, 201 when a resource was created, and 204 when the operation succeeded without a response body. A 201 response should normally identify the new resource with a Location header. Use 400 for malformed input, 401 when authentication is required, 403 when an authenticated caller lacks permission, 404 when the resource is absent, 405 when the resource does not allow that method, and 409 when current state conflicts with the operation.
Do not catch every failure and return 200 with an error string. Clients, caches, gateways, and monitoring tools use status codes to understand the exchange. Likewise, do not return a Java stack trace or database exception as an API body.
@WebServlet("/redirect-demo")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String action = req.getParameter("action");
if ("redirect".equals(action)) {
// sendRedirect: client-side redirect (new HTTP request)
// URL changes in browser, request attributes are lost
// Can redirect to external URLs
resp.sendRedirect("https://example.com");
// OR: resp.sendRedirect(req.getContextPath() + "/home");
} else if ("forward".equals(action)) {
// RequestDispatcher.forward: server-side forward (same request)
// URL does NOT change in browser, request attributes are preserved
// Can only forward within the same web application
req.setAttribute("message", "Forwarded from RedirectServlet");
RequestDispatcher rd = req.getRequestDispatcher("/WEB-INF/views/result.jsp");
rd.forward(req, resp);
} else if ("include".equals(action)) {
// RequestDispatcher.include: includes another resource's output
// Both servlets contribute to the response
RequestDispatcher rd = req.getRequestDispatcher("/header.jsp");
rd.include(req, resp);
resp.getWriter().println("<p>Main content here</p>");
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown action");
}
}
}
PUT expresses the desired complete state of a resource at the target URI. Repeating the same PUT should leave the resource in the same intended state. PATCH carries a set of partial changes, so its idempotency depends on the patch format and operation. Incrementing a counter is not idempotent; setting a display name to a fixed value can be.
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import java.io.IOException;
@WebServlet("/api/users/*")
public class UserServlet extends HttpServlet {
@Override
protected void doPatch(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String id = req.getPathInfo();
if (id == null || id.length() < 2) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "User id is required");
return;
}
// Parse and validate a patch document before applying it.
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
}
One servlet instance commonly handles concurrent requests. Keep request-specific values in local variables and move shared mutable state into thread-safe services or a database transaction. Idempotent method semantics do not automatically make an implementation race-free.
Networks can fail after the server changes state but before the client receives the response. For commands that may be retried, consider an idempotency key, a uniqueness constraint, or an operation identifier so the server can recognize duplicate submissions.
Try this next
0 of 3 completed
Yes. HttpServlet in Servlet 6.1 defines doPatch(HttpServletRequest, HttpServletResponse).
It can still be idempotent because repeating the request leaves the resource absent; identical response codes are not required.
No. Use 201 when a resource is created; commands or processing operations may return another success status.
Explore 500+ free tutorials across 20+ languages and frameworks.