Могут быть случаи, когда ваш 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 @Compress public 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.1 Accept-Encoding: gzip,deflate Accept: application/json, application/xml Host: localhost: 8888 Connection: Keep-Alive User-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 OK Content-Type: application/json Content-Encoding: gzip Content-Length: 409 Server: 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 . |