Корневые классы ресурсов — это 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) {
// Сохранение сообщения
}