如何在嵌入式Jetty中实现多级路径的高级路由?
Hey there! Great question—nested path routing like /user/1234/cart/addsomething is a common need, and embedded Jetty gives you a few solid ways to pull this off. Let’s walk through the most practical approaches, depending on how you want to structure your code.
1. Servlet 3.0+ Path Matching with Manual Parameter Extraction
This is the simplest approach if you want to stick with plain old Servlets. You can use wildcard url-patterns to catch the base path, then parse the rest of the URL to extract parameters and route to the right logic.
Example Code:
First, set up your embedded Jetty server and register Servlets with wildcard patterns:
public class EmbeddedJettyServer { public static void main(String[] args) throws Exception { Server server = new Server(8080); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); server.setHandler(context); // Register a servlet to handle all /user/* requests context.addServlet(new ServletHolder(new UserServlet()), "/user/*"); // Register a cart servlet for nested paths context.addServlet(new ServletHolder(new CartServlet()), "/user/*/cart/*"); server.start(); server.join(); } }
Then, in the UserServlet, extract the user ID and forward to the CartServlet if needed:
public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String pathInfo = request.getPathInfo(); // Gets "/1234/cart/addsomething" if (pathInfo == null || pathInfo.length() <= 1) { response.getWriter().write("Invalid user request"); return; } String[] pathSegments = pathInfo.split("/"); String userId = pathSegments[1]; // Extracts "1234" request.setAttribute("userId", userId); // Pass userId to downstream servlets // Check if the next segment is "cart" and forward if (pathSegments.length >= 3 && "cart".equals(pathSegments[2])) { String forwardPath = String.join("/", Arrays.copyOfRange(pathSegments, 2, pathSegments.length)); request.getRequestDispatcher("/user/" + userId + "/" + forwardPath).forward(request, response); } else { response.getWriter().write("Profile for user: " + userId); } } }
Finally, the CartServlet handles the action:
public class CartServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userId = (String) request.getAttribute("userId"); String pathInfo = request.getPathInfo(); // Gets "/addsomething" String action = pathInfo.split("/")[1]; // Extracts "addsomething" if ("addsomething".equals(action)) { response.getWriter().write("Adding item to cart for user: " + userId); } else { response.getWriter().write("Unsupported cart action: " + action + " for user " + userId); } } }
2. Use Jetty's Built-in RoutingHandler
If you want more flexible, declarative routing without Servlets, Jetty’s RoutingHandler is perfect. It lets you define route patterns with named parameters directly.
Example Code:
public class JettyRoutingServer { public static void main(String[] args) throws Exception { Server server = new Server(8080); RoutingHandler routingHandler = new RoutingHandler(); // Match /user/{userId}/cart/{action} and handle directly routingHandler.addRoute( RouteMatchers.path("/user/{userId}/cart/{action}"), (request, response) -> { // Extract named parameters from the route String userId = RouteData.get(request, "userId"); String action = RouteData.get(request, "action"); response.setContentType("text/plain"); response.getWriter().write("User " + userId + " performing cart action: " + action); return true; // Signal that the request was handled } ); // Add another route for /user/{userId} routingHandler.addRoute( RouteMatchers.path("/user/{userId}"), (request, response) -> { String userId = RouteData.get(request, "userId"); response.getWriter().write("User profile: " + userId); return true; } ); server.setHandler(routingHandler); server.start(); server.join(); } }
3. Integrate JAX-RS (e.g., Jersey) for RESTful Routing
If you’re building a REST API, using JAX-RS with Jersey (which integrates seamlessly with Jetty) gives you clean, annotation-driven routing for nested paths.
Example Code:
First, define your JAX-RS resource class with @Path annotations:
@Path("/user") public class UserResource { @GET @Path("/{userId}/cart/{action}") public Response handleCartAction( @PathParam("userId") String userId, @PathParam("action") String action ) { String message = String.format("User %s is %s in their cart", userId, action); return Response.ok(message).build(); } @GET @Path("/{userId}") public Response getUserProfile(@PathParam("userId") String userId) { return Response.ok("Profile for user: " + userId).build(); } }
Then set up Jetty to use Jersey:
public class JettyJerseyServer { public static void main(String[] args) throws Exception { Server server = new Server(8080); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); // Register Jersey's servlet container ServletHolder jerseyServlet = context.addServlet( org.glassfish.jersey.servlet.ServletContainer.class, "/*" ); jerseyServlet.setInitOrder(0); // Tell Jersey about your resource class jerseyServlet.setInitParameter( "jersey.config.server.provider.classnames", UserResource.class.getCanonicalName() ); server.setHandler(context); server.start(); server.join(); } }
Which Approach Should You Choose?
- Plain Servlets: Best if you want minimal dependencies and already work with Servlets.
- RoutingHandler: Great for lightweight, flexible routing without the overhead of Servlets or JAX-RS.
- JAX-RS: Ideal for REST APIs where you want standardized, annotation-driven routing and request/response handling.
内容的提问来源于stack exchange,提问作者Mr. SoUndso




