Как избежать бесконечного цикла при отображении сервлетов JSP

#java #jsp #rest #servlets #restful-url

#java #jsp #rest #сервлеты #restful-url

Вопрос:

У меня возникла эта проблема, недавно я прочитал об архитектуре REST, и в этом есть смысл, поэтому я хотел бы создать веб-приложение RESTful.

Теперь я следую шаблону Front Controller, который означает, что все сопоставления URL-адресов переходят к controller.java сервлету, я сопоставляю их по определенным URL-адресам, а не с помощью /* подстановочного знака, контроллер реализует четыре HTTP-метода POST , GET , PUT DELETE , service каждый метод вызывает метод controllers HttpServletRequest , и там я определяю на основе pathInfo и, , действия для выполнения. 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);
    }
  

Я столкнулся с конкретной проблемой при загрузке определенного порядка с помощью URI /orders/* , он отображается на сервлет контроллера, выполняется действие, и я загружаю соответствующий порядок, действие возвращает 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, также я хотел бы сделать это с помощью сервлетов, я слышал о UrlRewriteFilter, возможно, в будущем, но прямо сейчас, как бы это было сделано с использованием «Plain Vanilla», поскольку я использую шаблон Front Controller, нужно ли добавлять дополнительный сервлет в /orders/ URI?

Любая помощь или идеи действительно ценятся.


ПРАВКА 1:

Вставил исходный код контроллера, очень простой, у меня есть подозрения, что способ, которым service метод вызывает все переопределенные do[Method] части сервлета, запускает цикл, и что это может быть решено путем их разделения.

Комментарии:

1. Вы говорите о сервлетах, но симптомы указывают на то, что вы используете фильтр, который перехватывает все три метода отправки вместо (по умолчанию) REQUEST . Или я вас совершенно неправильно понимаю?

2. Единственный фильтр, который я использую, — это фильтр аутентификации, который применяется независимо с помощью /* но я не понимаю, как это запускает запрос снова и снова, поскольку он только проверяет, вошел ли пользователь в систему.

3. Что ж.. Я бы хотел помочь, но вам действительно нужно отобразить минимально необходимый фрагмент кода, который показывает только необработанные классы / методы API сервлетов, которые воспроизводят именно эту проблему, вместо вашего частного / доморощенного API, о котором больше никто не знает. Кстати, это просто частное учебное упражнение? Если это так, то не обращайте на меня внимания и просто продолжайте, если нет, то я рекомендую использовать существующий API вместо доморощенного, такого как замечательный JAX-RS API. Он также построен поверх Servlet API. Смотрите также vogella.de/articles/REST/article.html

4. Привет, @BalusC, спасибо за ваш ответ, да, это учебное упражнение.

5. Чтобы быть уверенным, view.getResource() во время пересылки / включения возвращает URL, который соответствует шаблону URL этого сервлета? Этого не должно быть. Обычно вы хотели бы иметь эти ресурсы JSP в /WEB-INF папке.

Ответ №1:

Реализовать RESTful HTTP-интерфейс на Java намного проще, используя реализацию JAX-RS, такую как RESTEasy или Jersey.

Использование внешнего контроллера для отправки запросов к нужному ресурсу — хороший подход, это именно тот подход, которого придерживаются эти фреймворки 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 аннотацию), и как легко вы можете анализировать значения из URL с помощью @PathParam . Поскольку вы получаете всю готовую обработку / диспетчеризацию / синтаксический анализ, вы можете сосредоточиться на тех элементах вашего приложения, которые специфичны для вашего домена (например, на том, что содержит order).

Комментарии:

1. Используется ли это реализация Jersey или только JAX-RS API, поскольку все мои поиски JAX-RS указывают на Jersey, может быть, вы можете прояснить некоторые моменты, является ли JAX-RS автономным или я должен использовать реализацию Jersey?.

2. JAX-RS — это спецификация, часть JEE. Вы можете думать о JAX-RS как о наборе интерфейсов / аннотаций без реализации. Чтобы создать приложение, вам нужно выбрать реализацию JAX-RS. RESTEasy — это одна реализация (принадлежит JBoss), Jersey — другая (Jersey — эталонная реализация, доступная на java.net). Существуют также другие, менее популярные реализации, такие как Apache CXF и Restlet. В каком контейнере вы развертываете (и в какой версии)? Возможно, ваш контейнер уже включает реализацию JAX-RS, которую вы можете использовать.

3. Я развертываюсь на apache-tomcat 6.0.26, я прочитал несколько сравнений, хотя мне не нравится название Jersey, кажется, что оно намного проще, чем RESTlet (у которого классное название), не хотите поделиться опытом?

4. Я бы порекомендовал RESTEasy 2.1.0.GA . Это очень легко настроить, особенно в простом контейнере сервлетов, таком как tomcat. Он также зрелый и имеет много точек интеграции. Я использовал только клиентский API Jersey (который превосходен), но я полагаю, что это почти то же самое. Restlet существует уже давно, у него был свой собственный альтернативный подход (не JAX-RS) к реализации ресурсов REST, который я использовал. Совсем недавно была добавлена реализация, совместимая с JAX-RS, но я думаю, что библиотека в целом показывает свой возраст.

5. @Triz: Выбирать реализацию, основанную только на имени, безумно. Выбирайте его на основе предоставляемых им функций и степени обслуживания / поддержки со стороны команды разработчиков и сообщества.