Статьи

Разработка сервисов RESTful с использованием Apache CXF

Вступление

Как вы уже знаете, есть два способа разработки веб-сервиса

  1. Простой протокол доступа к объектам (SOAP)
  2. Представительный государственный трансферт (REST)

Прежде чем приступить к созданию веб-службы на основе REST с использованием Apache CXF, посмотрим, что такое REST. REST — это не технология и, конечно, не какой-то стандарт. Это просто архитектурный стиль, который описывает, как написать веб-сервис определенным образом. Этот стиль был определен неким Роем Филдингом (кольцевые колокольчики? Да, вы правильно догадались, он один из архитекторов HTTP) в 2000 году. Главный герой архитектуры REST — это Ресурс, который может быть уникально идентифицирован с помощью Унифицированного идентификатора ресурса или URI . Состояние ресурса в любой данный момент времени представлено документом и называется представлением ресурса. Клиент может обновить состояние ресурса, передав представление вместе с запросом. Новое представление теперь возвращается клиенту вместе с ответом. Представление содержит информацию в таких форматах, как html, xml, JSON и т. Д., Которые принимаются ресурсом. Ресурс, который придерживается правил архитектуры REST, называется ресурсом RESTfull, а веб-сервис, который придерживается этого правила, называется веб-сервисом RESTfull.

Создайте проект, содержащий ваш веб-сервис

Я обычно делаю свою веб-разработку в struts2 + spring, используя maven Strut2 стартовый архетип для создания своего веб-проекта. Чтобы использовать CXF в моем проекте, я добавляю следующие зависимости к своему POM

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>${cxf.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>${cxf.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http-jetty</artifactId>
    <version>${cxf.version}</version>
</dependency>

Пользователи не maven могут найти подробности зависимостей, которые будут добавлены по следующей ссылке: http://cxf.apache.org/docs/using-cxf-with-maven.html . CXF можно скачать прямо здесь: http://cxf.apache.org/download.html

Как создать веб-сервис CXF RESTfull?

Предположим, вы хотите создать веб-сервис RESTfull, используя CXF для управления книгами на вашей личной книжной полке. Как правило, вы хотите выполнить следующие действия на книжной полке

  1. Добавить книгу
  2. Обновить информацию о книге
  3. Удалить книгу с полки
  4. Получить книгу
  5. Получить список книг
  6. Получить список книг по имени автора

Следующие шаги необходимы для создания такого сервиса

  1. Создайте BookVO, BookList (объект значения) для передачи в виде представления в запросе и ответе.
  2. Свяжите объекты с запросом и ответом.
  3. Создайте класс реализации службы для принятия запроса и генерации ответа.
  4. Регистрация вашего веб-сервиса с контейнером CXF.
  5. Разверните сервис в веб-контейнере.
  6. Создайте клиентов для вызова методов в службе.

Получение исходного кода для этого урока

Я передал исходные файлы для этого урока в SVN.

Примечание. Оба проекта являются проектами ItelliJ maven, поэтому вы можете напрямую импортировать их в вашу среду IntelliJ или скопировать файлы вручную в другую среду IDE.

Создать BookVO (объект значения) для передачи в качестве представления в запросе и ответе.

Класс BookVO

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
package com.aranin.weblog4j.vo;
 
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.Serializable;
 
@XmlRootElement(name="Book")
public class BookVO implements Serializable{
 
private long bookId;
private String bookName;
private String author;
 
public long getBookId() {
return bookId;
}
 
public void setBookId(long bucketId) {
this.bookId = bookId;
}
 
public String getBookName() {
return bookName;
}
 
public void setBookName(String bookName) {
this.bookName = bookName;
}
 
public String getAuthor() {
return author;
}
 
public void setAuthor(String author) {
this.author = author;
}
}

Класс BookList

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
package com.aranin.weblog4j.vo;
 
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;
 
@XmlRootElement(name="BookList")
public class BookList {
private List<BookVO> bookList;
 
public List<BookVO> getBookList() {
if(bookList == null){
bookList = new ArrayList<BookVO>();
}
return bookList;
}
 
public void setBookList(List<BookVO> bookList) {
this.bookList = bookList;
}
}

Привязать объект данных, т.е. BookVO с запросом и ответом

Чтобы связать BookVO с запросом или ответом, его необходимо сериализовать в потоки XML или JSON. Сериализация должна выполняться с использованием одного из компонентов привязки данных. CXF использует JAXB для компонента привязки данных по умолчанию. JaXB использует аннотацию @XmlRootElement для сопоставления объекта данных с xml. Вы можете увидеть использование аннотации XmlRootElement в приведенном выше коде.

Создайте класс реализации сервиса для принятия запроса и генерации ответа.

Давайте посмотрим, как выглядит веб-сервис CXF RestFull. Мы создадим класс BookService, который будет выполнять операции добавления, обновления, удаления и получения на BookSelf.

Класс BookService

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.aranin.weblog4j.services.rest;
 
import com.aranin.weblog4j.hashdb.HashDB;
import com.aranin.weblog4j.vo.BookVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
 
/**
 * Created by IntelliJ IDEA.
 * User: Niraj Singh
 * Date: 3/13/13
 * Time: 3:58 PM
 * To change this template use File | Settings | File Templates.
 */
public class BookService {
 
protected final Logger log = LoggerFactory.getLogger(BookService.class);
 
    @POST
    @Path("/getbook/{name}")
    @Produces({"application/xml","application/json"})
    @Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
    public Response getBucket(@PathParam("name") String name) {
        log.debug("name : " + name);
        BookVO bookVO = null;
        try {
            bookVO = HashDB.getBook(URLDecoder.decode(name, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
 
        if(bookVO == null){
            return Response.status(Response.Status.BAD_REQUEST).build();
        }else{
            return Response.ok(bookVO).build();
        }
    }
 
    @POST
    @Path("/addbook")
    @Produces({"application/xml","application/json"})
    @Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
    public Response addBook(@FormParam("name") String bookName,
                            @FormParam("author") String author) {
        log.debug("inside addBook");
        BookVO bookVO = new BookVO();
        bookVO.setBookName(bookName);
        bookVO.setAuthor(author);
        HashDB.insertBook(bookVO);
        if(HashDB.getBook(bookName) == null){
            return Response.status(Response.Status.BAD_REQUEST).build();
        }else{
            return Response.ok(bookVO).build();
        }
 
    }
}

Вы можете увидеть два метода в классе BookService getBook и addBook. Это сервисные методы для получения и добавления книги. Остальные методы удаления обновлений и т. Д. Могут быть написаны таким же образом. Теперь давайте посмотрим, что означают различные аннотации и вызов метода.

  • @POST — это означает, что сервис получает только POST-запрос.
  • @Path — это путь к веб-сервису. Таким образом, веб-сервис может быть вызван с использованием следующего Url <base_url> / bookservice / getbook / {name} для выборки, <base_url> / bookservice / addbook для добавления.
  • @Produces — указывает тип сгенерированного ответа MIME. В нашем случае это и приложение / xml, и приложение / json.
  • @Consumes — указывает MIME-тип запроса, который может использовать этот сервис.

Регистрация вашего веб-сервиса с контейнером CXF.

Одна из замечательных особенностей CXF заключается в том, что он использует конфигурацию на основе пружины для регистрации своих конечных точек веб-службы, поэтому давайте создадим beans.xml в WEB-INF и настроим CXF в web.xml. Для этого сначала нам нужно подключить файл beans.xml для загрузки контейнером Spring.

1
2
3
4
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml,/WEB-INF/applicationContext.xml</param-value>
</context-param>

Во-вторых, загрузите реестр CXFServlet в web.xml.

01
02
03
04
05
06
07
08
09
10
11
12
13
<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
 
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

Теперь откройте ваш bean.xml и зарегистрируйте конечную точку книжного сервиса.

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
<?xml version="1.0" encoding="UTF-8"?>
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xmlns:jaxrs="http://cxf.apache.org/jaxrs"
    xsi:schemaLocation="
 
 
 
 
 
 
 
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
 
    <jaxws:endpoint
      id="bookShelfService"
      implementor="com.aranin.weblog4j.services.BookShelfServiceImpl"
      address="/bookshelfservice" />
 
    <bean id="bookserviceclass" class="com.aranin.weblog4j.services.rest.BookService"/>
 
    <jaxrs:server id="bookservice" address="/bookservice">
        <jaxrs:serviceBeans>
        <ref bean="bookserviceclass" />
        </jaxrs:serviceBeans>
    </jaxrs:server>
 
</beans>

Теперь ваш веб-сервис готов. Создайте свое веб-приложение и разверните его в любом контейнере сервлетов.

Создание клиента для вашего веб-сервиса

Клиенты могут быть созданы разными способами, я использовал Apache Http Components для написания своего клиента. Библиотеки можно найти по адресу http://hc.apache.org/httpclient-3.x/ .

Пользователь Maven может вытащить банку компонентов Http, используя следующие

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
 
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.1.3</version>
<scope>compile</scope>
</dependency>
 
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.1.3</version>
<scope>compile</scope>
</dependency>

Теперь, чтобы вызвать веб-сервис, я создал класс утилит под названием DemoRestClient.

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.aranin.weblog4j.client;
 
import com.aranin.weblog4j.vo.BookVO;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
 
import java.net.URLEncoder;
 
/**
 * Created by IntelliJ IDEA.
 * User: Niraj Singh
 * Date: 3/13/13
 * Time: 4:15 PM
 * To change this template use File | Settings | File Templates.
 */
public class DemoRestClient {
    public static void main(String[] args){
        DemoRestClient restClient = new DemoRestClient();
        try {
            //restClient.addBook("Naked Sun", "Issac Asimov");
            restClient.getBook("Naked Sun");
        } catch (Exception e) {
            e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
        }
 
    }
 
    public BookVO getBook(String bookName) throws Exception {
 
        String output = null;
        try{
            String url = "http://localhost:8080/weblog4jdemo/bookservice/getbook/";
 
            url = url + URLEncoder.encode(bookName, "UTF-8");
 
            HttpClient client = new HttpClient();
            PostMethod mPost = new PostMethod(url);
            client.executeMethod( mPost );
            Header mtHeader = new Header();
            mtHeader.setName("content-type");
            mtHeader.setValue("application/x-www-form-urlencoded");
            mtHeader.setName("accept");
            mtHeader.setValue("application/xml");
            mPost.addRequestHeader(mtHeader);
            client.executeMethod(mPost);
            output = mPost.getResponseBodyAsString( );
            mPost.releaseConnection( );
            System.out.println("out : " + output);
        }catch(Exception e){
            throw new Exception("Exception in retriving group page info : " + e);
        }
        return null;
    }
 
    public void addBook(String bookName, String author) throws Exception {
 
        String output = null;
        try{
            String url = "http://localhost:8080/weblog4jdemo/bookservice/addbook";
            HttpClient client = new HttpClient();
            PostMethod mPost = new PostMethod(url);
            mPost.addParameter("name", "Naked Sun");
            mPost.addParameter("author", "Issac Asimov");
            Header mtHeader = new Header();
            mtHeader.setName("content-type");
            mtHeader.setValue("application/x-www-form-urlencoded");
            mtHeader.setName("accept");
            mtHeader.setValue("application/xml");
            //mtHeader.setValue("application/json");
            mPost.addRequestHeader(mtHeader);
            client.executeMethod(mPost);
            output = mPost.getResponseBodyAsString( );
            mPost.releaseConnection( );
            System.out.println("output : " + output);
        }catch(Exception e){
        throw new Exception("Exception in adding bucket : " + e);
        }
 
    }
 
}

Запустите этот клиент, чтобы увидеть выходные данные вашего веб-сервиса. Прямо сейчас он отправит xmloutput, поскольку заголовок подтверждения ответа — «application / xml». Вы можете изменить его на application / json, чтобы получить вывод json.

Это все люди. Это очень простое введение в разработку веб-сервиса RestFull с использованием apache CXF, есть еще много чего, что можно исследовать. Счастливого пути, до свидания. Пожалуйста, оставьте некоторые комментарии, и если вы прочитаете это, чтобы вдохновить меня.