【问题标题】:Avoiding the endless loop in JSP servlet mapping避免 JSP servlet 映射中的无限循环
【发布时间】:2011-08-05 03:53:28
【问题描述】:

我遇到了这个问题,最近我读到了REST 架构,它非常有意义,所以我想实现一个 RESTful Web 应用程序。

现在,我关注 Front Controller pattern,这意味着所有 URL 映射都转到 controller.java servlet,我按特定 URL 映射,而不是使用 /* 通配符, 控制器实现POST,GET,PUT,DELETE这四种HTTP方法,每个方法调用控制器service方法,我根据HttpServletRequestpathInfo确定要执行的动作. 控制器.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);
    }

我在通过 URI /orders/* 加载特定订单时遇到了一个特定问题,它被映射到控制器 servlet,执行该操作并加载适当的顺序该操作返回一个 View.java


//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;
    }
}

然后根据返回视图的getDispatchMethod()调度请求。

现在,这里是触发循环的地方,我使用以下 URL,myapp/orders/78965 /orders/* 被映射到 controller.java 执行适当的操作,并通过 pathInfo() 返回的视图找到正确的顺序是new View(View.DispatchMethod.FORWARD,"order_details.jsp") 问题在于,使用三种可用的调度方法REDIRECT,FORWARD and INCLUDE 会在URL 上重新触发请求,依此类推,我永远无法到达呈现数据的order_details.jsp

那么,您将如何避免循环,因为我想保留显示订单号的 URI,我使用 forward 方法,另外,我想使用 servlet,我听说过 UrlRewriteFilter也许在未来,但现在,由于我使用的是前端控制器模式,如何使用“Plain Vanilla”来完成,是否有必要在 /orders/ URI 中添加一个额外的 servlet ?

非常感谢任何帮助或见解。


编辑 1:

粘贴了控制器的源代码,一个非常基本的,我怀疑service方法调用servlet的所有覆盖do[Method]的方式触发了循环,它可以通过以下方式解决分裂他们。

【问题讨论】:

  • 您在谈论 servlet,但症状表明您正在使用一个过滤器,该过滤器挂钩所有三种调度方法,而不是(默认)REQUEST。还是我完全误解了你?
  • 我使用的唯一过滤器是身份验证过滤器,它是使用/* 应用的,但我不知道它是如何反复触发请求的,因为它只检查用户是否登录.
  • 好吧.. 我很乐意提供帮助,但您确实必须显示最低限度的必要代码 sn-p,它仅显示重现此确切问题的原始 Servlet API 类/方法,而不是您的其他人不知道的私有/本土 API。顺便问一下,这只是一个私人学习练习吗?如果是这样,那就别管我了,继续吧,如果不是,那么我建议采用现有的 API 而不是自行开发的 API,例如出色的 JAX-RS API。它也建立在 Servlet API 之上。另见vogella.de/articles/REST/article.html
  • 嗨,@BalusC,谢谢你的回复,是的,这是一个学习练习。
  • 可以肯定的是,在转发/包含期间view.getResource() 是否返回与此 servlet 的 URL 模式匹配的 URL?它不应该。您通常希望将这些 JSP 资源放在 /WEB-INF 文件夹中。

标签: java jsp rest servlets restful-url


【解决方案1】:

使用 RESTEasyJersey 等 JAX-RS 实现在 Java 中实现 RESTful HTTP 接口要容易得多。

使用前端控制器将请求分派到正确的资源是一种很好的方法,这正是这些 JAX-RS 框架所采用的方法。我担心你可能会在这里重新发明轮子,编写一个定制的 URL 解析和分派机制,当它可以现成的时候。

JAX-RS 是一种公开资源的轻量级方式。通过使用几个简单的注释,您可以公开一个 REST 接口,而无需任何管道。例如:

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);

    }

}

您可以看到将此类附加到 URL 路径是多么简单(使用 @Path 注释),以及使用 @PathParam 从 URL 解析值是多么容易。由于您获得了现成的所有管道/调度/解析,因此您可以专注于特定于您的域的应用程序位(例如订单包含的内容)。

【讨论】:

  • 这是使用 Jersey 实现还是仅使用 JAX-RS API,因为我对 JAX-RS 的所有搜索都指向 Jersey,也许您可​​以清除其中的一些要点,是 JAX-RS独立还是我必须使用 Jersey 实现?。
  • JAX-RS 是一个规范,是 JEE 的一部分。您可以将 JAX-RS 视为一组没有实现的接口/注释。要创建应用程序,您需要选择 JAX-RS 的实现。 RESTEasy 是一种实现(由 JBoss 拥有),Jersey 是另一种(Jersey 是参考实现,可在 java.net 上获得)。还有其他不太流行的实现,例如 Apache CXF 和 Restlet。您要部署到哪个容器(以及哪个版本)?您的容器可能已经包含您可以使用的 JAX-RS 实现。
  • 我正在部署到 apache-tomcat 6.0.26,我已经阅读了一些比较,虽然我讨厌 Jersey 名称,但它似乎比 RESTlet 简单得多(它有一个很酷的名称) ,愿意分享经验吗?
  • 我推荐RESTEasy 2.1.0.GA。它非常容易配置,尤其是在像 tomcat 这样的简单 servlet 容器中。它也很成熟,有很多集成点。我只使用了 Jersey 的客户端 API(非常好),但我想它是一样的。 Restlet 已经存在很长时间了,它有自己的替代方法(非 JAX-RS)来实现 REST 资源,我使用过。最近它添加了一个符合 JAX-RS 的实现,但我认为这个库总体上已经显示出它的年龄了。
  • @Triz:仅根据名称选择实现是疯狂的。根据它提供的功能以及开发团队和社区的维护/支持程度来选择它。
猜你喜欢
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
  • 2019-05-01
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多