SOA专题
使用Apache CXF开发RESTful服务
开发Web服务有两种方式:
- Simple Object Access Protocol (SOAP)
- Representational State Transfer (REST)
REST不是技术,也不是某种标准。它仅仅是一种架构风格,REST架构的原理是可以通过统一资源标识符或URI唯一标识一个资源。在任何给定的时间点,一个资源的状态是通过文档展现称为代表的资源。客户可以通过请求展现来更新资源的状态。新展现将返回到客户端。资源的接受展现包含的格式有HTML,XML,JSON等。资源遵循REST架构规则被称为一个restfull资源,Web服务遵循这个规则被称为restfull Web服务。
那么如何创建一个CXF的Web服务呢?以管理书架上书籍为例子,经过下面步骤:
- 增加一本书
- 更新书的信息
- 从书架上删除一本书
- 得到一本书
- 得到书的列表
- 根据作者得到书籍列表
下面创建的服务如下:
- 创建BookVO, BookList (值对象)作为展现传递
- 绑定对象到请求和响应
- 创建服务实现类接受请求和产生响应。
- 注册你的服务到CXF容器
- 部署服务到Web容器
- 创建客户端调用这个服务
Web服务源码下载:http://subversion.assembla.com/svn/weblog4j/Weblog4jDemo/trunk
客户端代码下载:http://subversion.assembla.com/svn/weblog4j/DemoClient/trunk
下面看一下服务的代码:
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();
}
}
}
元注解的几种解释:
- @POST –服务接受处理POST 请求
- @Path – web服务的URL路径,抓取URL Url <base_url>/bookservice/getbook/{name} , 增加:<base_url>/bookservice/addbook
- @Produces – 指示响应的MIME类型,在案例中是 application/xml 和 application/json.
- @Consumes – 这个服务能消费的请求的MIME类型
注册服务到CXF容器, 在 WEB-INF 创建一个beans.xml,然后跟随Spring启动即可:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<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>
注册Spring在web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml,/WEB-INF/applicationContext.xml</param-value>
</context-param>
在web.xml加载 CXFServlet:
<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>
客户端调用代码:
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);
}
}
}