Могут быть случаи, когда ваш REST API предоставляет очень длинные ответы, и мы все знаем, насколько важны скорость передачи и пропускная способность на мобильных устройствах / сетях. Я думаю, что это первая точка оптимизации производительности, которую необходимо учитывать при разработке REST API, поддерживающих мобильные приложения. Угадай, что? Поскольку ответы являются текстовыми, мы можем их сжать. И с сегодняшней мощью смартфонов и планшетов распаковка их на стороне клиента не должна быть большой проблемой … Так что в этой статье я покажу, как ВЫБРАТЬ СЕКТОРНО, сжимая свои ответы REST API, если вы построили их на Java с Джерси , что такое эталонная реализация JAX-RS (и не только)…
1. Джерси фильтры и перехватчики
Что ж, благодаря мощным функциям фильтров и перехватчиков Джерси, реализация довольно проста. Принимая во внимание, что фильтры в первую очередь предназначены для манипулирования параметрами запроса и ответа, такими как заголовки HTTP, URI и / или методы HTTP, перехватчики предназначены для манипулирования сущностями посредством манипулирования потоками ввода / вывода сущностей.
Вы видели силу фильтров в моих сообщениях:
- Как добавить поддержку CORS на стороне сервера в Java с Джерси , где я показал, как CORS-включить REST API
и - Как войти в Spring с SLF4J и Logback , где я показал, как регистрировать запросы и ответы от REST API
но для сжатия буду использовать GZip WriterInterceptor . Перехватчик записи используется в случаях, когда сущность записывается в «провод», что на стороне сервера, как в данном случае, означает при записи ответной сущности.
1.1. GZip Writer Interceptor
Итак, давайте посмотрим на наш GZip Writer Interceptor:
GZip Writer Interceptor
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package org.codingpedia.demo.rest.interceptors;import java.io.IOException;import java.io.OutputStream;import java.util.zip.GZIPOutputStream;import javax.ws.rs.WebApplicationException;import javax.ws.rs.core.MultivaluedMap;import javax.ws.rs.ext.WriterInterceptor;import javax.ws.rs.ext.WriterInterceptorContext;@Provider@Compresspublic class GZIPWriterInterceptor implements WriterInterceptor { @Override public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { MultivaluedMap<String,Object> headers = context.getHeaders(); headers.add("Content-Encoding", "gzip"); final OutputStream outputStream = context.getOutputStream(); context.setOutputStream(new GZIPOutputStream(outputStream)); context.proceed(); }} |
Замечания:
- он реализует
WriterInterceptor, который является интерфейсом для перехватчиковjavax.ws.rs.ext.MessageBodyWriter.writeToтела сообщения, которые обертывают вызовыjavax.ws.rs.ext.MessageBodyWriter.writeTo - провайдеры, реализующие контракт
WriterInterceptorдолжны быть или программно зарегистрированы во время выполнения JAX-RS, или должны быть аннотированы аннотацией @Provider для автоматического обнаружения во время выполнения JAX-RS во время фазы сканирования провайдера. -
@Compress— аннотация привязки имени, которую мы обсудим более подробно в следующем абзаце. - «Перехватчик получает выходной поток из WriterInterceptorContext и устанавливает новый, который является оберткой GZIP исходного выходного потока. После выполнения всех перехватчиков выходной поток, последний раз установленный в WriterInterceptorContext, будет использоваться для сериализации объекта. В приведенном выше примере байты объекта будут записаны в GZIPOutputStream, который будет сжимать данные потока и записывать их в исходный поток вывода. Исходный поток всегда является потоком, который записывает данные в «провод». Когда перехватчик используется на сервере, исходный поток вывода является потоком, в который записываются данные в основной контейнерный поток сервера, который отправляет ответ клиенту ». [2]
- «Переопределенный метод aroundWriteTo () получает WriterInterceptorContext в качестве параметра. Этот контекст содержит методы получения и установки параметров заголовка, свойств запроса, объекта, потока объектов и других свойств ». [2]; когда вы сжимаете свой ответ, вы должны установить заголовок «Content-Encoding» на «gzip»
1.2. Сжать аннотацию
Фильтры и перехватчики могут быть привязаны к имени . Привязка имени — это концепция, которая позволяет сказать среде выполнения JAX-RS, что определенный фильтр или перехватчик будет выполняться только для определенного метода ресурса. Когда фильтр или перехватчик ограничен только определенным методом ресурса, мы говорим, что он привязан к имени . Фильтры и перехватчики, которые не имеют такого ограничения, называются глобальными . В нашем случае мы создали аннотацию @Compress:
Сжать аннотацию
|
01
02
03
04
05
06
07
08
09
10
11
|
package org.codingpedia.demo.rest.interceptors;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import javax.ws.rs.NameBinding;//@Compress annotation is the name binding annotation@NameBinding@Retention(RetentionPolicy.RUNTIME)public @interface Compress {} |
и использовал его для пометки методов на ресурсах, которые должны быть сжаты ( например, при получении всех подкастов с PodcastsResource ):
Использование аннотации @Compress по методу ресурса
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
@Component@Path("/podcasts")public class PodcastsResource { @Autowired private PodcastService podcastService; ........................... /* * *********************************** READ *********************************** */ /** * Returns all resources (podcasts) from the database * * @return * @throws IOException * @throws JsonMappingException * @throws JsonGenerationException * @throws AppException */ @GET @Compress @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public List<Podcast> getPodcasts( @QueryParam("orderByInsertionDate") String orderByInsertionDate, @QueryParam("numberDaysToLookBack") Integer numberDaysToLookBack) throws IOException, AppException { List<Podcast> podcasts = podcastService.getPodcasts( orderByInsertionDate, numberDaysToLookBack); return podcasts; } ...........................} |
2. Тестирование
2.1. SoapUI
Что ж, если вы тестируете с SOAPui, вы можете отправить следующий запрос к PodcastsResource .
Запрос:
Пример запроса
|
1
2
3
4
5
6
|
GET http://localhost:8888/demo-rest-jersey-spring/podcasts/?orderByInsertionDate=DESC HTTP/1.1Accept-Encoding: gzip,deflateAccept: application/json, application/xmlHost: localhost:8888Connection: Keep-AliveUser-Agent: Apache-HttpClient/4.1.1 (java 1.5) |
Отклик:
GZipped ответ json, автоматически разархивируется SOAPui
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
HTTP/1.1 200 OKContent-Type: application/jsonContent-Encoding: gzipContent-Length: 409Server: Jetty(9.0.7.v20131107)[ { "id": 2, "title": "Quarks & Co - zum Mitnehmen", "description": "Quarks & Co: Das Wissenschaftsmagazin", "insertionDate": "2014-10-29T10:46:13.00+0100" }, { "id": 1, "title": "- The Naked Scientists Podcast - Stripping Down Science", "linkOnPodcastpedia": "http://www.podcastpedia.org/podcasts/792/-The-Naked-Scientists-Podcast-Stripping-Down-Science", "feed": "feed_placeholder", "description": "The Naked Scientists flagship science show brings you a lighthearted look at the latest scientific breakthroughs, interviews with the world top scientists, answers to your science questions and science experiments to try at home.", "insertionDate": "2014-10-29T10:46:02.00+0100" }] |
SOAPui распознает заголовок Content-Type: gzip , который мы добавили в GZIPWriterInterceptor и автоматически распаковывает ответ и отображает его для чтения человеческим глазом.
Ну вот и все. Вы узнали, как Джерси позволяет сжимать ответы API REST.
Совет: Если вы действительно хотите научиться проектировать и реализовывать REST API в Java, прочитайте следующее руководство — Разработка и реализация REST API на Java с использованием Jersey и Spring
| Ссылка: | Как сжать ответы в Java REST API с помощью GZip и Jersey от нашего партнера по JCG Адриана Матеи в блоге Codingpedia.org . |