Avoiding the endless loop in JSP servlet mapping

2019-05-31 11:33发布

问题:

I've got this issue, recently I read about the REST arquitecture and it makes a perfect sense, so I'd like to achieve a RESTful web application.

Now, I'm following the Front Controller pattern that means that all of the URL mappings go to the controller.java servlet, I map the by specific URLs, not by using the /* wildcard, the controller implements the four HTTP methods POST,GET,PUT,DELETE, each method calls the controllers service method and there I determine based on the HttpServletRequest and pathInfo the action to execute. Controller.java


 @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        IAction action;
        View view;
        try {
            action = ActionFactory.produceAction(req);
            view = action.execute(req, resp);
            switch (view.getDispatchMethod()) {
                case REDIRECT:
                    resp.sendRedirect(resp.encodeURL(view.getResource()));
                    break;
                case FORWARD:
                    req.getRequestDispatcher(view.getResource()).forward(req, resp);
                    break;
                case INCLUDE:
                    req.getRequestDispatcher(view.getResource()).include(req,resp);
                    break;
                default:
            }
        } catch (ActionFailedException uae) {
            req.setAttribute("ActionName", "Action");
            req.setAttribute("FailCause", uae.getMessage());
            req.getRequestDispatcher(VIEW_FAIL.getResource()).forward(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        this.service(req, resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.service(req, resp);
    }

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.service(req, resp);
    }

    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.service(req, resp);
    }

I've run into a particular issue when loading a specific order by the URI /orders/*, it is mapped to the controller servlet, the the action is executed and I load the appropriate order the action returns a View.java class


//ommited accessors and mutators for brevety.
public class View {
public enum DispatchMethod {
        INCLUDE, FORWARD, REDIRECT
    }

    private DispatchMethod dispatchMethod;
    private String resource;

    public View(DispatchMethod dispatchMethod, String resource) {
        this.dispatchMethod = dispatchMethod;
        this.resource = resource;
    }
}

Then the request is dispatched according to the getDispatchMethod() of the returned view.

Now, here is where the loop gets triggered, I use the following URL, myapp/orders/78965 /orders/* gets mapped to controller.java the appropriate action is executed and the correct order is found by the pathInfo() the returned view is new View(View.DispatchMethod.FORWARD,"order_details.jsp") the problem is that with the three available dispatch methods REDIRECT,FORWARD and INCLUDE a request is re-triggered on the URL and so on and on and on I never reach the order_details.jsp that renders the data.

So, how would you avoid the looping, as I'd like to preserve the URI displaying the order number I use the forward method, also, I'd like to do it using servlets, I've heard of the UrlRewriteFilter maybe in the future, but right now, how would it be done using "Plain Vanilla" since I'm using the Front Controller pattern, will it be necessary to add an additional servlet in the /orders/ URI ?

Any help or insights is truly appreciated.


EDIT 1:

Pasted the source code of the controller, a very basic one, I have my suspicions that the way the service method calls all of the overriden do[Method] of the servlet is triggering the loop and that it may be solved by splittig them.

回答1:

Implementing a RESTful HTTP interface in Java is a lot easier using a JAX-RS implementation like RESTEasy or Jersey.

Using a Front Controller to dispatch requests to the right resource is a good approach, it's exactly the approach taken by these JAX-RS frameworks. I fear you may be re-inventing the wheel here by writing a bespoke URL parsing and dispatching mechanism when this can be taken off-the-shelf.

JAX-RS is a lightweight way to expose resources. By using a couple of simple annotations you can expose a REST interface without any plumbing required. For example:

public class Order {

    @GET
    @Path("/orders/{orderId}")
    @Produces("text/html")
    public void getOrder(@Context HttpServletResponse response,
                         @Context HttpServletRequest request,
                         @PathParam("orderId") String orderId) throws ServletException, IOException {

        // ... create view and add to request here

        request.getRequestDispatcher("orders.jsp").forward(request, response);

    }

}

You can see how simple it is to attach this class to a URL path (using the @Path annotation), and how easily you can parse values from the URL using @PathParam. Since you get all the plumbing/dispatching/parsing off-the-shelf, you can concentrate on the bits of your app that are specific to your domain (such as what an order contains).