Недавно мне пришлось написать некоторый Java-код для использования сервисов REST через HTTP. Я решил использовать клиентские библиотеки RestEasy , фреймворка, который я использую большую часть времени для предоставления сервисов REST в Java, поскольку он также реализует официальную спецификацию JAX-RS . Я очень доволен подходом, основанным на аннотациях, который определяется в спецификации, и он делает доступ к сервисам REST очень приятной задачей. Но, к сожалению, я не могу сказать, что мне нравится клиентский API таким же образом. Если вам повезло, что вы можете создать прокси-клиент на основе интерфейса, реализованного службой, это неплохо:
1
2
3
4
5
6
7
|
import org.jboss.resteasy.client.ProxyFactory; ... // this initialization only needs to be done once per VM RegisterBuiltin.register(ResteasyProviderFactory.getInstance()); client.myBusinessMethod( 'hello world' ); |
Я согласен, что иметь Proxy-клиент, похожий на JAX-WS, хорошо. Но в большинстве случаев, когда мы используем веб-сервис REST, у нас нет Java-интерфейса для импорта. Все те Twitter, Google или любые другие доступные общественные службы отдыха — это всего лишь конечные точки HTTP. В этих случаях можно использовать RestEasy, полагаясь на API клиентского запроса RestEasy:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
request.header( 'custom-header' , 'value' ); // We're posting XML and a JAXB object request.body( 'application/xml' , someJaxb); // we're expecting a String back ClientResponse<String> response = request.post(String. class ); if (response.getStatus() == 200 ) // OK! { String str = response.getEntity(); } |
На мой взгляд, это очень многословный способ получить то, что чаще всего, просто куча строк из Интернета. И это становится еще хуже, если вам нужно включить информацию аутентификации:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
// Configure HttpClient to authenticate preemptively // by prepopulating the authentication data cache. // 1. Create AuthCache instance AuthCache authCache = new BasicAuthCache(); // 2. Generate BASIC scheme object and add it to the local auth cache BasicScheme basicAuth = new BasicScheme(); authCache.put( 'com.bluemonkeydiamond.sippycups' , basicAuth); // 3. Add AuthCache to the execution context BasicHttpContext localContext = new BasicHttpContext(); localContext.setAttribute(ClientContext.AUTH_CACHE, authCache); // 4. Create client executor and proxy httpClient = new DefaultHttpClient(); ApacheHttpClient4Executor executor = new ApacheHttpClient4Executor(httpClient, localContext); client = ProxyFactory.create(BookStoreService. class , url, executor); |
Я обнаружил, что Rest-assured предоставляют гораздо более приятный API для написания клиентских вызовов. Официально цель проекта — создать систему тестирования и валидации ; и большинство учебных пособий, посвященных этим аспектам, например, недавнее руководство Хайко Раппа: http://pilhuhn.blogspot.nl/2013/01/testing-rest-apis-with-rest-assured.html . Вместо этого я предлагаю использовать его как инструмент разработки, чтобы очень быстро экспериментировать и писать вызовы REST. Что важно знать об отдыхе:
- он реализует специфичный для домена язык благодаря плавному API
- это одна зависимость Maven
- он почти полностью раскрывает общий стиль для объектов ответов xml и json
- это зависит от Apache Commons Client
Итак, я покажу вам несколько реальных примеров использования и оставлю вам хорошую ссылку, если вы хотите узнать больше. Как и большинство DSL на Java, он работает лучше, если статически импортировать наиболее важные объекты :
1
2
|
import static com.jayway.restassured.RestAssured.*; import static com.jayway.restassured.matcher.RestAssuredMatchers.*; |
Базовое использование:
1
|
|
Это возвращает:
1
2
3
|
< errors > < error code = "34" >Sorry, that page does not exist</ error > </ errors > |
Ой, какая-то ошибка . Да, нам нужно передать некоторый параметр:
1
2
3
|
with() .parameter( 'screen_name' , 'resteasy' ) |
Это возвращает:
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
36
37
38
|
< user > < id >27016395</ id > < name >Resteasy</ name > < screen_name >resteasy</ screen_name > < location ></ location > < profile_image_url >http://a0.twimg.com/sticky/default_profile_images/default_profile_0_normal.png</ profile_image_url > < profile_image_url_https >https://si0.twimg.com/sticky/default_profile_images/default_profile_0_normal.png</ profile_image_url_https > < url ></ url > < description >jboss.org/resteasy JBoss/Red Hat REST project</ description > < protected >false</ protected > < followers_count >244</ followers_count > < profile_background_color >C0DEED</ profile_background_color > < profile_text_color >333333</ profile_text_color > < profile_link_color >0084B4</ profile_link_color > < profile_sidebar_fill_color >DDEEF6</ profile_sidebar_fill_color > < profile_sidebar_border_color >C0DEED</ profile_sidebar_border_color > < friends_count >1</ friends_count > < created_at >Fri Mar 27 14:39:52 +0000 2009</ created_at > < favourites_count >0</ favourites_count > < utc_offset ></ utc_offset > < time_zone ></ time_zone > < profile_background_image_url >http://a0.twimg.com/images/themes/theme1/bg.png</ profile_background_image_url > < profile_background_image_url_https >https://si0.twimg.com/images/themes/theme1/bg.png</ profile_background_image_url_https > < profile_background_tile >false</ profile_background_tile > < profile_use_background_image >true</ profile_use_background_image > < geo_enabled >false</ geo_enabled > < verified >false</ verified > < statuses_count >8</ statuses_count > < lang >en</ lang > < contributors_enabled >false</ contributors_enabled > < is_translator >false</ is_translator > < listed_count >21</ listed_count > < default_profile >true</ default_profile > < default_profile_image >true</ default_profile_image > ... </ user > |
Намного лучше! Теперь предположим, что нам нужен только токен этого большого XML-кода String :
1
2
3
4
|
with() .parameter( 'screen_name' , 'resteasy' ) .path( 'user.profile_image_url' ) |
И вот наш вывод:
Что если это был ответ JSON ?
1
2
3
|
И вот наш вывод:
{"id":27016395,"id_str":"27016395","name":"Resteasy","screen_name":"resteasy","location":"","url":null,"description":"jboss.org\/resteasy\n\nJBoss\/Red Hat REST project","protected":false,"followers_count":244,"friends_count":1,"listed_count":21,"created_at":"Fri Mar 27 14:39:52 +0000 2009","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":8,"lang":"en","status":{"created_at":"Tue Mar 23 14:48:51 +0000 2010","id":10928528312,"id_str":"10928528312","text":"Doing free webinar tomorrow on REST, JAX-RS, RESTEasy, and REST-*. Only 40 min, so its brief. http:\/\/tinyurl.com\/yz6xwek","source":"web","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorited":false,"retweeted":false},"contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/sticky\/default_profile_images\/default_profile_0_normal.png","profile_image_url_https":"https:\/\/si0.twimg.com\/sticky\/default_profile_images\/default_profile_0_normal.png","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":true,"following":null,"follow_request_sent":null,"notifications":null}
И тот же интерфейс понимает навигацию объекта JSON. Обратите внимание, что выражение навигации не включает ‘user’, поскольку его не было в полном ответе json:
1
2
3
4
|
with() .parameter( 'screen_name' , 'resteasy' ) .path( 'profile_image_url' ) |
И вот наш вывод:
Теперь пример параметров пути :
1
2
3
|
Информация о http запросе :
1
2
|
|
Пример базовой аутентификации :
1
2
3
4
|
Пример загрузки формы из нескольких частей
1
2
3
|
with() .multiPart( 'file' , 'test.txt' , fileContent.getBytes()) .post( '/upload' ) |
Maven зависимость :
1
2
3
4
5
6
|
< dependency > < groupid >com.jayway.restassured</ groupid > < artifactid >rest-assured</ artifactid > < version >1.4</ version > < scope >test</ scope > </ dependency > |
А фрагмент Groovy, который можно вставить и выполнить непосредственно в groovyConsole благодаря Grapes, извлекает зависимости и автоматически добавляет их в путь к классам, что показывает поддержку JAXB :
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
|
@Grapes ([ @Grab ( 'com.jayway.restassured:rest-assured:1.7.2' ) ]) import static com.jayway.restassured.RestAssured.* import static com.jayway.restassured.matcher.RestAssuredMatchers.* import javax.xml.bind.annotation.* @XmlRootElement (name = 'user' ) @XmlAccessorType ( XmlAccessType.FIELD ) class TwitterUser { String id; String name; String description; String location; @Override String toString() { return 'Id: $id, Name: $name, Description: $description, Location: $location' } } println with().parameter( 'screen_name' , 'resteasy' ).get( 'http://api.twitter.com/1/users/show.xml' ).as(TwitterUser. class ) // |
Это лишь краткий список возможностей библиотеки, просто вы представляете, как легко с ней работать. Для дальнейших примеров я предлагаю вам прочитать официальные страницы здесь: https://code.google.com/p/rest-assured/wiki/Usage . Или другой хороший учебник с примером приложения для игры: http://www.hascode.com/2011/10/testing-restful-web-services-made-easy-using-the-rest-assured-framework
Ссылка: Java: будьте уверены (или отдохните очень легко) от нашего партнера JCG Паоло Антинори в блоге Someday Never Comes .