#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: Выбирать реализацию, основанную только на имени, безумно. Выбирайте его на основе предоставляемых им функций и степени обслуживания / поддержки со стороны команды разработчиков и сообщества.