Корневые классы ресурсов — это POJO, которые либо аннотированы @Path, либо имеют хотя бы один метод, аннотированный @Path, или указатель метода запроса, такие как @GET, @PUT, @POST или @DELETE. Методы ресурса — это методы класса ресурса, аннотированные указателем метода запроса. В этом разделе объясняется, как использовать JAX-RS для аннотирования классов Java для создания RESTful веб-сервисов.
Разработка RESTful веб-сервисов с JAX-RS
JAX-RS — это API Java, разработанный для упрощения разработки приложений, использующих архитектуру REST.
API JAX-RS использует аннотации Java для упрощения разработки RESTful веб-сервисов. Разработчики декорируют файлы классов Java аннотациями JAX-RS, чтобы задать ресурсы и действия, которые могут быть выполнены с этими ресурсами. Аннотации JAX-RS являются аннотациями времени выполнения. Следовательно, reflection во время выполнения сгенерирует вспомогательные классы и артефакты для ресурса. В архиве приложения Java EE, содержащем классы ресурсов JAX-RS, будут настроены ресурсы, сгенерированы вспомогательные классы и артефакты, а также ресурс, предоставленный клиентам путём развёртывания архива на сервере Java EE.
Таблица 30-1. Обзор аннотаций JAX-RS
Аннотация |
Описание |
@Path
|
Значение аннотации @Path представляет собой относительный путь URI, указывающий, где будет размещён класс Java: например, /helloworld. Вы также можете встраивать переменные в URI для создания шаблона пути URI. Например, вы можете запросить имя пользователя и передать его приложению в качестве переменной в URI: /helloworld/{username}. |
@GET
|
Аннотация @GET является указателем метода запроса и соответствует одноимённому HTTP-методу. Аннотированный таким образом метод Java будет обрабатывать HTTP GET-запросы. Поведение ресурса определяется HTTP-методом, на который отвечает ресурс. |
@POST
|
Аннотация @POST является указателем метода запроса и соответствует одноимённому HTTP-методу. Аннотированный таким образом метод Java будет обрабатывать HTTP POST-запросы. Поведение ресурса определяется HTTP-методом, на который отвечает ресурс. |
@PUT
|
Аннотация @PUT является указателем метода запроса и соответствует одноимённому HTTP-методу. Аннотированный таким образом метод Java будет обрабатывать HTTP PUT-запросы. Поведение ресурса определяется HTTP-методом, на который отвечает ресурс. |
@DELETE
|
Аннотация @DELETE является указателем метода запроса и соответствует одноимённому HTTP-методу. Аннотированный таким образом метод Java будет обрабатывать HTTP DELETE-запросы. Поведение ресурса определяется HTTP-методом, на который отвечает ресурс. |
@HEAD
|
Аннотация @HEAD является указателем метода запроса и соответствует одноимённому HTTP-методу. Аннотированный таким образом метод Java будет обрабатывать HTTP HEAD-запросы. Поведение ресурса определяется HTTP-методом, на который отвечает ресурс. |
@OPTIONS
|
Аннотация @OPTIONS является указателем метода запроса и соответствует одноимённому HTTP-методу. Аннотированный таким образом метод Java будет обрабатывать HTTP OPTIONS-запросы. Поведение ресурса определяется HTTP-методом, на который отвечает ресурс. |
@PATCH
|
Аннотация @PATCH является указателем метода запроса и соответствует одноимённому HTTP-методу. Аннотированный таким образом метод Java будет обрабатывать HTTP PATCH-запросы. Поведение ресурса определяется HTTP-методом, на который отвечает ресурс. |
@PathParam
|
Аннотация @PathParam — это тип параметра, который может быть извлечён для использования в классе ресурсов. Параметры пути URI извлекаются из URI запроса, а имена параметров соответствуют именам переменных шаблона пути URI, указанным в аннотации на уровне класса @Path. |
@QueryParam
|
Аннотация @QueryParam — это тип параметра, который может быть извлечён для использования в классе ресурсов. Параметры запроса извлекаются из URI запроса. |
@Consumes
|
Аннотация @Consumes используется для указания типов MIME, которые может принимать ресурс и которые были отправлены клиентом. |
@Produces
|
Аннотация @Produces используется для указания типов MIME, которые ресурс может создавать и отправлять обратно клиенту: например, «text/plain». |
@Provider
|
Аннотация @Provider используется для всего, что представляет интерес для среды выполнения JAX-RS, например MessageBodyReader и MessageBodyWriter. Для HTTP-запросов MessageBodyReader используется для сопоставления тела объекта HTTP-запроса с параметрами метода. Возвращаемое значение сопоставляется с телом объекта HTTP-ответа с помощью MessageBodyWriter. Если приложению необходимо предоставить дополнительные метаданные, такие как заголовки HTTP или другой код состояния, метод может вернуть Response-обёртку (wrapper) сущности и который может быть создан с использованием Response.ResponseBuilder. |
@ApplicationPath
|
Аннотация @ApplicationPath используется для назначения URL приложению. Путь, указанный в @ApplicationPath, является базовым URI для всех URI ресурсов, указанных в аннотациях @Path в классе ресурсов. Аннотация @ApplicationPath может быть применена только к дочернему классу javax.ws.rs.core.Application. |
Аннотация @Path и шаблоны пути URI
Аннотация @Path идентифицирует шаблон пути URI, на который отвечает ресурс, и указывается на уровне класса или метода ресурса. Значение аннотации @Path является частичным шаблоном пути URI относительно базового URI сервера, на котором развёрнут ресурс, корневого контекста приложения и шаблона URL, по которому отвечает среда JAX-RS времени выполнения.
Шаблоны пути URI — это URI с переменными, встроенными в синтаксис URI. Эти переменные подставляются во время выполнения, чтобы ресурс отвечал на запрос на основе замещённого URI. Переменные обозначаются фигурными скобками ({ и }). Например, посмотрите на следующую аннотацию @Path:
@Path("/users/{username}")
В этом примере пользователю предлагается ввести имя, а затем отвечает веб-сервис JAX-RS, настроенный для ответа на запросы к этому шаблону пути URI. Например, если пользователь вводит имя пользователя «Galileo», веб-сервис отвечает на следующий URL:
http://example.com/users/Galileo
Чтобы получить значение имени пользователя, можно использовать аннотацию @PathParam для параметра метода запроса, как показано в следующем примере кода:
@Path("/users/{username}")
public class UserResource {
@GET
@Produces("text/xml")
public String getUser(@PathParam("username") String userName) {
...
}
}
По умолчанию переменная URI должна соответствовать регулярному выражению "[^/]+?". Эту переменную можно настроить, указав другое регулярное выражение после имени переменной. Например, если имя пользователя должно состоять только из строчных и прописных буквенно-цифровых символов, переопределите регулярное выражение по умолчанию в определении переменной:
@Path("users/{username: [a-zA-Z][a-zA-Z_0-9]*}")
В этом примере переменная username будет соответствовать только именам пользователей, которые начинаются с одной заглавной или строчной буквы, а также с нуля или более буквенно-цифровых символов и символа подчеркивания. Если имя пользователя не соответствует этому шаблону, клиенту будет отправлен ответ 404 (Not Found).
Значение @Path не обязательно должно иметь начальную или конечную косую черту (/). Среда выполнения JAX-RS анализирует шаблоны пути URI одинаково, независимо от того, имеют ли они начальную или конечную косую черту.
Шаблон пути URI имеет одну или несколько переменных, каждое имя переменной заключено в фигурные скобки: { для начала имени переменной и } для её завершения. В предыдущем примере username является именем переменной. Во время выполнения ресурс, настроенный для ответа на предыдущий шаблон пути URI, попытается обработать данные URI, которые соответствуют расположению {username} в URI, в качестве переменных данных для username.
@Path("/{name1}/{name2}/")
public class SomeResource {
...
}
В этом примере по умолчанию используется шаблон URL для вспомогательного сервлета JAX-RS, указанный в web.xml:
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
Имя переменной может использоваться более одного раза в шаблоне пути URI.
Если символ в значении переменной будет конфликтовать с зарезервированными символами URI, конфликтующий символ должен быть URL-кодирован. Например, пробелы в значении переменной должны быть заменены на %20.
При определении шаблонов пути URI, будьте осторожны, чтобы результирующий URI после замены был валидным.
Таблица 32-2 перечисляет некоторые примеры переменных шаблона пути URI и того, как URI разрешаются после замены. В примерах используются следующие имена и значения переменных:
-
name1: james
-
name2: gatz
-
name3:
-
location: Main%20Street
-
question: why
Значение переменной name3 — пустая строка.
|
Таблица 32-2 Примеры шаблонов пути URI
Использование @Consumes и @Produces для настройки запросов и ответов
Информация, отправляемая ресурсу и затем возвращаемая клиенту, указывается как тип MIME в заголовках HTTP-запроса или ответа. Вы можете указать типы MIME, на которые ресурс может реагировать или которые может создавать, используя следующие аннотации:
-
javax.ws.rs.Consumes
-
javax.ws.rs.Produces
По умолчанию класс ресурсов может отвечать и генерировать все типы MIME, указанные в заголовках HTTP-запроса и ответа.
Здесь рассматриваются следующие темы:
Аннотация @Produces
Аннотация @Produces используется для указания типов MIME, которые ресурс может создавать и отправлять обратно клиенту. Если @Produces применяется на уровне класса, все методы в ресурсе могут создавать указанные типы MIME по умолчанию. При использовании на уровне метода аннотации переопределяют любые аннотации @Produces уровня класса.
Если никакие методы в ресурсе не могут создать содержимое указанного в запросе клиента типа MIME, среда выполнения JAX-RS отправляет обратно ошибку HTTP «406 Not Acceptable».
Значением @Produces является массив String типов MIME или разделённый запятыми список констант MediaType. Например:
@Produces({"image/jpeg,image/png"})
В следующем примере показано, как применить @Produces как на уровне класса, так и на уровне метода:
@Path("/myResource")
@Produces("text/plain")
public class SomeResource {
@GET
public String doGetAsPlainText() {
...
}
@GET
@Produces("text/html")
public String doGetAsHtml() {
...
}
}
Метод doGetAsPlainText по умолчанию использует тип MIME аннотации @Produces на уровне класса. Аннотация @Produces метода doGetAsHtml переопределяет параметр @Produces уровня класса и указывает, что метод может создавать HTML, а не простой текст.
@Produces также может использовать константы, определённые в классе javax.ws.rs.core.MediaType, чтобы указать тип MIME. Например, указание MediaType.APPLICATION_XML эквивалентно указанию «application/xml».
@Produces(MediaType.APPLICATION_XML)
@GET
public Customer getCustomer() { ... }
Если класс ресурсов способен создавать более одного типа MIME, выбранный метод ресурса будет соответствовать наиболее подходящему типу MIME из числа объявленных клиентом. Более конкретно, заголовок Accept HTTP-запроса объявляет, что является наиболее приемлемым. Например, если заголовок Accept имеет значение Accept: text/plain, будет вызван метод doGetAsPlainText. В качестве альтернативы, если заголовок Accept равен Accept: text/plain;q=0.9, text/html, который объявляет, что клиент может принимать типы MIME text/plain и text/html, но предпочитающие последнее, будет вызван метод doGetAsHtml.
В одном объявлении @Produces может быть объявлено несколько типов MIME. В следующем примере кода показано, как это сделать:
@Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {
...
}
Метод doGetAsXmlOrJson будет вызван, если допустим любой из типов MIME — application/xml или application/json. Если оба одинаково приемлемы, будет выбран тот, который указан первым. Предыдущие примеры явно указывают на типы MIME для ясности. Можно ссылаться на константы, которые исключают возможность опечатки. Для получения дополнительной информации см. документацию API для значений константных полей javax.ws.rs.core.MediaType.
Аннотация @Consumes
Аннотация @Consumes используется для указания того, какие типы MIME ресурс может принимать от клиента. Если @Consumes применяется на уровне класса, все методы ответа по умолчанию принимают указанные типы MIME. При использовании на уровне метода @Consumes переопределяет любые аннотации @Consumes уровня класса.
Если ресурс не может использовать тип MIME из запроса клиента, среда выполнения JAX-RS отправляет обратно ошибку HTTP 415 («Unsupported Media Type»).
Значением @Consumes является массив String допустимых типов MIME или разделённый запятыми список констант MediaType. Например:
@Consumes({"text/plain,text/html"})
@Consumes({MediaType.TEXT_PLAIN,MediaType.TEXT_HTML})
В следующем примере показано, как применить @Consumes как на уровне класса, так и на уровне метода:
@Path("/myResource")
@Consumes("multipart/related")
public class SomeResource {
@POST
public String doPost(MimeMultipart mimeMultipartData) {
...
}
@POST
@Consumes("application/x-www-form-urlencoded")
public String doPost2(FormURLEncodedProperties formData) {
...
}
}
Метод doPost по умолчанию использует тип MIME аннотации @Consumes на уровне класса. Метод doPost2 переопределяет аннотацию @Consumes уровня класса, указывая, что он может принимать данные формы в кодировке URL.
Если никакие методы ресурсов не могут ответить на запрошенный тип MIME, клиенту возвращается ошибка HTTP 415 («Unsupported Media Type»).
Пример HelloWorld, рассмотренный ранее в этом разделе, можно изменить, чтобы установить сообщение с помощью @Consumes, как показано в следующем примере кода:
@POST
@Consumes("text/html")
public void postHtml(String message) {
// Сохранение сообщения
}
В этом примере метод Java будет использовать представления, идентифицированные типом MIME text/plain. Обратите внимание, что метод ресурса возвращает void. Это означает, что представление не возвращается и что будет возвращён ответ с кодом состояния HTTP 204 («No Content»).
Параметры метода ресурса могут быть аннотированы аннотациями для извлечения информации из запроса. В предыдущем примере было представлено использование параметра @PathParam для извлечения параметра пути из компонента пути URL запроса, который соответствует пути, объявленному в @Path.
Могут быть извлечены следующие типы параметров для использования в классе ресурсов:
-
Запрос
-
Путь URI
-
Форма
-
Cookie
-
Заголовок
-
Матрица
Параметры запроса извлекаются из URI запроса и задаются с помощью аннотации javax.ws.rs.QueryParam в аргументах параметра метода. В следующем примере демонстрируется использование @QueryParam для извлечения параметров запроса из компонента Query URL запроса:
@Path("smooth")
@GET
public Response smooth(
@DefaultValue("2") @QueryParam("step") int step,
@DefaultValue("true") @QueryParam("min-m") boolean hasMin,
@DefaultValue("true") @QueryParam("max-m") boolean hasMax,
@DefaultValue("true") @QueryParam("last-m") boolean hasLast,
@DefaultValue("blue") @QueryParam("min-color") ColorParam minColor,
@DefaultValue("green") @QueryParam("max-color") ColorParam maxColor,
@DefaultValue("red") @QueryParam("last-color") ColorParam lastColor
) { ... }
Если параметр запроса step существует в URI запроса, значение step будет извлечено и проанализировано как 32-разрядное целое число со знаком и присвоено параметру метода step. Если step не существует, параметру метода step будет присвоено значение по умолчанию, равное 2, как объявлено в аннотации @DefaultValue. Если значение step не может быть приведено к 32-разрядному целое число со знаком, возвращается ответ HTTP 400 («Client Error»).
Пользовательские типы Java могут использоваться в качестве параметров запроса. В следующем примере кода показан класс ColorParam, использованный в предыдущем примере параметра запроса:
public class ColorParam extends Color {
public ColorParam(String s) {
super(getRGB(s));
}
private static int getRGB(String s) {
if (s.charAt(0) == '#') {
try {
Color c = Color.decode("0x" + s.substring(1));
return c.getRGB();
} catch (NumberFormatException e) {
throw new WebApplicationException(400);
}
} else {
try {
Field f = Color.class.getField(s);
return ((Color)f.get(null)).getRGB();
} catch (Exception e) {
throw new WebApplicationException(400);
}
}
}
}
Конструктор для ColorParam принимает один параметр String.
И @QueryParam, и @PathParam могут использоваться только на следующих типах Java.
-
Все примитивные типы, кроме char.
-
Все классы-обёртки примитивных типов, кроме Character.
-
Любой класс с конструктором, который принимает один аргумент String.
-
Любой класс со статическим методом с именем valueOf(String), который принимает один аргумент String.
-
List<T>, Set<T> или SortedSet<T>, где T соответствует уже перечисленным критериям. Иногда параметры могут содержать более одного значения для одного и того же имени. Если это так, эти типы могут быть использованы для получения всех значений.
Если @DefaultValue не используется вместе с @QueryParam, а параметр запроса отсутствует в запросе, это значение будет пустой коллекцией для List, Set или SortedSet, null для других типов объектов и значением по умолчанию для примитивных типов.
Параметры пути URI извлекаются из URI запроса, а имена параметров соответствуют именам переменных шаблона пути URI, указанным в аннотации на уровне класса @Path. Параметры URI указываются с помощью аннотации javax.ws.rs.PathParam в аргументах параметра метода. В следующем примере показано, как использовать переменные @Path и аннотацию @PathParam в методе:
@Path("/{username}")
public class MyResourceBean {
...
@GET
public String printUsername(@PathParam("username") String userId) {
...
}
}
В предыдущем фрагменте имя переменной шаблона пути URI username указывается в качестве параметра для метода printUsername. Аннотация @PathParam устанавливается для переменной с именем username. Во время выполнения перед вызовом printUsername значение username извлекается из URI и приводится к String. Результирующая String затем доступна методу в виде переменной userId.
Если переменная шаблона пути URI не может быть приведена к указанному типу, среда выполнения JAX-RS возвращает клиенту ошибку HTTP 400 («Bad Request»). Если аннотация @PathParam не может быть приведена к указанному типу, среда выполнения JAX-RS возвращает клиенту ошибку HTTP 404 («Not Found»).
Параметр @PathParam и другие аннотации параметров (@MatrixParam, @HeaderParam, @CookieParam и @FormParam) подчиняются тем же правилам, что и @QueryParam.
Параметры cookie, указанные путём аннотирования параметра javax.ws.rs.CookieParam, извлекают информацию из файлов cookie, объявленных в заголовках HTTP, связанных с файлами cookie. Параметры заголовка, указанные аннотированием параметра javax.ws.rs.HeaderParam, извлекают информацию из заголовков HTTP. Параметры матрицы, указанные аннотированием параметра javax.ws.rs.MatrixParam, извлекают информацию из сегментов пути URL.
Параметры формы, указанные путём аннотирования параметра javax.ws.rs.FormParam, извлекают информацию из запроса с типом MIME application/x-www-form-urlencoded и соответствует кодировке, указанной в формах HTML, как описано в http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1. Этот параметр очень полезен для извлечения информации, отправляемой POST в HTML-формах.
В следующем примере извлекается параметр формы name из данных формы POST:
@POST
@Consumes("application/x-www-form-urlencoded")
public void post(@FormParam("name") String name) {
// Сохранение сообщения
}
Чтобы получить общее отображение (Map) имён параметров и их значений из запроса, используйте следующий код:
@GET
public String get(@Context UriInfo ui) {
MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
MultivaluedMap<String, String> pathParams = ui.getPathParameters();
}
Следующий метод извлекает имена и значения параметров заголовка и cookie в отображение (Map):
@GET
public String get(@Context HttpHeaders hh) {
MultivaluedMap<String, String> headerParams = hh.getRequestHeaders();
Map<String, Cookie> pathParams = hh.getCookies();
}
В общем, @Context может использоваться для получения контекстных типов Java, связанных с запросом или ответом.
Для параметров формы можно сделать следующее:
@POST
@Consumes("application/x-www-form-urlencoded")
public void post(MultivaluedMap<String, String> formParams) {
// Сохранение сообщения
}