使用 CXF 开发 REST 服务

REST 全称是 Representational State Transfer(表述性状态转移),它是 Roy Fielding 博士在 2000 年写的一篇关于软件架构风格的论文,此文一出,威震四方!许多知名互

联网公司开始采用这种轻量级 Web 服务,大家习惯将其称为 RESTful Web Services,或简称 REST 服务。

那么 REST 到底是什么呢?

REST 本质上是使用 URL 来访问资源的一种方式。众所周知,URL 就是我们平常使用的请求地址了,其中包括两部分:请求方式 与 请求路径,比较常见的请求方式是 GET 与

POST,但在 REST 中又提出了几种其它类型的请求方式,汇总起来有六种:GET、POST、PUT、DELETE、HEAD、OPTIONS。尤其是前四种,正好与 CRUD(增删改查)

四种操作相对应:GET(查)、POST(增)、PUT(改)、DELETE(删),这正是 REST 的奥妙所在!

实际上,REST 是一个“无状态”的架构模式,因为在任何时候都可以由客户端发出请求到服务端,最终返回自己想要的数据。也就是说,服务端将内部资源发布 REST 服务,客

户端通过 URL 来访问这些资源,这不就是 SOA 所提倡的“面向服务”的思想吗?所以,REST 也被人们看做是一种轻量级的 SOA 实现技术,因此在企业级应用与互联网应用中

都得到了广泛使用。

在 Java 的世界里,有一个名为 JAX-RS 的规范,它就是用来实现 REST 服务的,目前已经发展到了 2.0 版本,也就是 JSR-339 规范,如果您想深入研究 REST,请深入阅读

此规范。

JAX-RS 规范目前有以下几种比较流行的实现技术:

Jersey:https://jersey.java.net/

Restlet:http://restlet.com/

RESTEasy:http://resteasy.jboss.org/

CXF:http://cxf.apache.org/

本文以 CXF 为例,我努力用最精炼的文字,让您快速学会如何使用 CXF 开发 REST 服务,此外还会将 Spring 与 CXF 做一个整合,让开发更加高效!

那么还等什么呢?咱们一起出发吧!

1. 使用 CXF 发布与调用 REST 服务

第一步:添加 Maven 依赖

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"  
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0  
  5.          http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  6.   
  7.     <modelVersion>4.0.0</modelVersion>  
  8.   
  9.     <groupId>demo.ws</groupId>  
  10.     <artifactId>rest_cxf</artifactId>  
  11.     <version>1.0-SNAPSHOT</version>  
  12.   
  13.     <properties>  
  14.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  15.         <cxf.version>3.0.0</cxf.version>  
  16.         <jackson.version>2.4.1</jackson.version>  
  17.     </properties>  
  18.   
  19.     <dependencies>  
  20.         <!-- CXF -->  
  21.         <dependency>  
  22.             <groupId>org.apache.cxf</groupId>  
  23.             <artifactId>cxf-rt-frontend-jaxrs</artifactId>  
  24.             <version>${cxf.version}</version>  
  25.         </dependency>  
  26.         <dependency>  
  27.             <groupId>org.apache.cxf</groupId>  
  28.             <artifactId>cxf-rt-transports-http-jetty</artifactId>  
  29.             <version>${cxf.version}</version>  
  30.         </dependency>  
  31.         <!-- Jackson -->  
  32.         <dependency>  
  33.             <groupId>com.fasterxml.jackson.jaxrs</groupId>  
  34.             <artifactId>jackson-jaxrs-json-provider</artifactId>  
  35.             <version>${jackson.version}</version>  
  36.         </dependency>  
  37.     </dependencies>  
  38.   
  39. </project>  
以上添加了 CXF 关于 REST 的依赖包,并使用了 Jackson 来实现 JSON 数据的转换。

第二步:定义一个 REST 服务接口

[java]  view plain  copy
  1. package demo.cxf.rest_cxf;  
  2.   
  3. import java.util.List;  
  4. import java.util.Map;  
  5. import javax.ws.rs.Consumes;  
  6. import javax.ws.rs.DELETE;  
  7. import javax.ws.rs.FormParam;  
  8. import javax.ws.rs.GET;  
  9. import javax.ws.rs.POST;  
  10. import javax.ws.rs.PUT;  
  11. import javax.ws.rs.Path;  
  12. import javax.ws.rs.PathParam;  
  13. import javax.ws.rs.Produces;  
  14. import static javax.ws.rs.core.MediaType.*;  
  15.   
  16. public interface ProductService {  
  17.   
  18.     @GET  
  19.     @Path("/products")  
  20.     @Produces(APPLICATION_JSON)  
  21.     List<Product> retrieveAllProducts();  
  22.   
  23.     @GET  
  24.     @Path("/product/{id}")  
  25.     @Produces(APPLICATION_JSON)  
  26.     Product retrieveProductById(@PathParam("id"long id);  
  27.   
  28.     @POST  
  29.     @Path("/products")  
  30.     @Consumes(APPLICATION_FORM_URLENCODED)  
  31.     @Produces(APPLICATION_JSON)  
  32.     List<Product> retrieveProductsByName(@FormParam("name") String name);  
  33.   
  34.     @POST  
  35.     @Path("/product")  
  36.     @Consumes(APPLICATION_JSON)  
  37.     @Produces(APPLICATION_JSON)  
  38.     Product createProduct(Product product);  
  39.   
  40.     @PUT  
  41.     @Path("/product/{id}")  
  42.     @Consumes(APPLICATION_JSON)  
  43.     @Produces(APPLICATION_JSON)  
  44.     Product updateProductById(@PathParam("id"long id, Map<String, Object> fieldMap);  
  45.   
  46.     @DELETE  
  47.     @Path("/product/{id}")  
  48.     @Produces(APPLICATION_JSON)  
  49.     Product deleteProductById(@PathParam("id"long id);  
  50. }  
以上 ProductService 接口中提供了一系列的方法,在每个方法上都使用了 JAX-RS 提供的注解,主要包括以下三类:

请求方式注解,包括:@GET、@POST、@PUT、@DELETE

请求路径注解,包括:@Path,其中包括一个路径参数

数据格式注解,包括:@Consumes(输入)、@Produces(输出),可使用 MediaType 常量

相关参数注解,包括:@PathParam(路径参数)、@FormParam(表单参数),此外还有 @QueryParam(请求参数)

针对 updateProductById 方法,简单解释一下:

该方法将被 PUT:/product/{id} 请求来调用,请求路径中的 id 参数将映射到 long id 参数上,请求体中的数据将自动转换为 JSON 格式并映射到 Map<String, Object> fieldMap 参数上,返回的 Product 类型的数据将自动转换为 JSON 格式并返回到客户端。

注意:由于 Product 类与 ProductService 接口的实现类并不是本文的重点,因此省略了,本文结尾处会给出源码链接。

第三步:使用 CXF 发布 REST 服务

[java]  view plain  copy
  1. package demo.cxf.rest_cxf;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;  
  6. import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;  
  7. import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;  
  8. import org.codehaus.jackson.jaxrs.JacksonJsonProvider;  
  9.   
  10. public class Server {  
  11.   
  12.     public static void main(String[] args) {  
  13.         // 添加 ResourceClass  
  14.         List<Class<?>> resourceClassList = new ArrayList<Class<?>>();  
  15.         resourceClassList.add(HelloServiceImpl.class);  
  16.         resourceClassList.add(ProductServiceImpl.class);  
  17.   
  18.         // 添加 ResourceProvider  
  19.         List<ResourceProvider> resourceProviderList = new ArrayList<ResourceProvider>();  
  20.         resourceProviderList.add(new SingletonResourceProvider(new HelloServiceImpl()));  
  21.         resourceProviderList.add(new SingletonResourceProvider(new ProductServiceImpl()));  
  22.   
  23.         // 添加 Provider  
  24.         List<Object> providerList = new ArrayList<Object>();  
  25.         providerList.add(new JacksonJsonProvider());  
  26.   
  27.         // 发布 REST 服务  
  28.         JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();  
  29.         factory.setAddress("http://localhost:8080/ws/rest");  
  30.         factory.setResourceClasses(resourceClassList);  
  31.         factory.setResourceProviders(resourceProviderList);  
  32.         factory.setProviders(providerList);  
  33.         factory.create();  
  34.         System.out.println("rest ws is published");  
  35.     }  
  36. }  
CXF 提供了一个名为 org.apache.cxf.jaxrs.JAXRSServerFactoryBean 的类,专用于发布 REST 服务,只需为该类的实例对象指定四个属性即可:

Address:REST 基础地址

ResourceClasses:一个或一组相关的资源类,即接口对应的实现类(注意:REST 规范允许资源类没有接口)

ResourceProviders:资源类对应的 Provider,此时使用 CXF 提供的 org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider 进行装饰

Providers:REST 服务所需的 Provider,此时使用了 Jackson 提供的 org.codehaus.jackson.jaxrs.JacksonJsonProvider,用于实现 JSON 数据的序列化与反序列化

运行以上 Server 类,将以 standalone 方式发布 REST 服务,下面我们通过客户端来调用以发布的 REST 服务。

第四步:使用 CXF 调用 REST 服务

首先添加如下 Maven 依赖:

[html]  view plain  copy
  1. <dependency>  
  2.     <groupId>org.apache.cxf</groupId>  
  3.     <artifactId>cxf-rt-rs-client</artifactId>  
  4.     <version>${cxf.version}</version>  
  5. </dependency>  
CXF 提供了三种 REST 客户端,下面将分别进行展示。

第一种:JAX-RS 1.0 时代的客户端

[java]  view plain  copy
  1. package demo.ws.rest_cxf;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import org.apache.cxf.jaxrs.client.JAXRSClientFactory;  
  6. import org.codehaus.jackson.jaxrs.JacksonJsonProvider;  
  7.   
  8. public class JAXRSClient {  
  9.   
  10.     public static void main(String[] args) {  
  11.         String baseAddress = "http://localhost:8080/ws/rest";  
  12.   
  13.         List<Object> providerList = new ArrayList<Object>();  
  14.         providerList.add(new JacksonJsonProvider());  
  15.   
  16.         ProductService productService = JAXRSClientFactory.create(baseAddress, ProductService.class, providerList);  
  17.         List<Product> productList = productService.retrieveAllProducts();  
  18.         for (Product product : productList) {  
  19.             System.out.println(product);  
  20.         }  
  21.     }  
  22. }  
本质是使用 CXF 提供的 org.apache.cxf.jaxrs.client.JAXRSClientFactory 工厂类来创建 ProductService 代理对象,通过代理对象调用目标对象上的方法。客户端同样也需要使

用 Provider,此时仍然使用了 Jackson 提供的 org.codehaus.jackson.jaxrs.JacksonJsonProvider。

第二种:JAX-RS 2.0 时代的客户端

[java]  view plain  copy
  1. package demo.ws.rest_cxf;  
  2.   
  3. import java.util.List;  
  4. import javax.ws.rs.client.ClientBuilder;  
  5. import javax.ws.rs.core.MediaType;  
  6. import org.codehaus.jackson.jaxrs.JacksonJsonProvider;  
  7.   
  8. public class JAXRS20Client {  
  9.   
  10.     public static void main(String[] args) {  
  11.         String baseAddress = "http://localhost:8080/ws/rest";  
  12.   
  13.         JacksonJsonProvider jsonProvider = new JacksonJsonProvider();  
  14.   
  15.         List productList = ClientBuilder.newClient()  
  16.             .register(jsonProvider)  
  17.             .target(baseAddress)  
  18.             .path("/products")  
  19.             .request(MediaType.APPLICATION_JSON)  
  20.             .get(List.class);  
  21.         for (Object product : productList) {  
  22.             System.out.println(product);  
  23.         }  
  24.     }  
  25. }  
在 JAX-RS 2.0 中提供了一个名为 javax.ws.rs.client.ClientBuilder 的工具类,可用于创建客户端并调用 REST 服务,显然这种方式比前一种要先进,因为在代码中不再依赖 CXF API 了。
如果想返回带有泛型的 List<Product>,那么可以使用以下代码片段:
[java]  view plain  copy
  1. List<Product> productList = ClientBuilder.newClient()  
  2.     .register(jsonProvider)  
  3.     .target(baseAddress)  
  4.     .path("/products")  
  5.     .request(MediaType.APPLICATION_JSON)  
  6.     .get(new GenericType<List<Product>>() {});  
  7. for (Product product : productList) {  
  8.     System.out.println(product);  
  9. }  
第三种:通用的 WebClient 客户端
[java]  view plain  copy
  1. package demo.ws.rest_cxf;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import javax.ws.rs.core.GenericType;  
  6. import javax.ws.rs.core.MediaType;  
  7. import org.apache.cxf.jaxrs.client.WebClient;  
  8. import org.codehaus.jackson.jaxrs.JacksonJsonProvider;  
  9.   
  10. public class CXFWebClient {  
  11.   
  12.     public static void main(String[] args) {  
  13.         String baseAddress = "http://localhost:8080/ws/rest";  
  14.   
  15.         List<Object> providerList = new ArrayList<Object>();  
  16.         providerList.add(new JacksonJsonProvider());  
  17.   
  18.         List productList = WebClient.create(baseAddress, providerList)  
  19.             .path("/products")  
  20.             .accept(MediaType.APPLICATION_JSON)  
  21.             .get(List.class);  
  22.         for (Object product : productList) {  
  23.             System.out.println(product);  
  24.         }  
  25.     }  
  26. }  
CXF 还提供了一种更为简洁的方式,使用 org.apache.cxf.jaxrs.client.WebClient 来调用 REST 服务,这种方式在代码层面上还是相当简洁的。

如果想返回带有泛型的 List<Product>,那么可以使用以下代码片段:

[java]  view plain  copy
  1. List<Product> productList = WebClient.create(baseAddress, providerList)  
  2.     .path("/products")  
  3.     .accept(MediaType.APPLICATION_JSON)  
  4.     .get(new GenericType<List<Product>>() {});  
  5. for (Product product : productList) {  
  6.     System.out.println(product);  
  7. }  

2. 使用 Spring + CXF 发布 REST 服务

第一步:添加 Maven 依赖

[html]  view plain  copy
  1. <project xmlns="http://maven.apache.org/POM/4.0.0"  
  2.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0  
  4.          http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  5.   
  6.     <modelVersion>4.0.0</modelVersion>  
  7.   
  8.     <groupId>demo.ws</groupId>  
  9.     <artifactId>rest_spring_cxf</artifactId>  
  10.     <version>1.0-SNAPSHOT</version>  
  11.     <packaging>war</packaging>  
  12.   
  13.     <properties>  
  14.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
  15.         <spring.version>4.0.6.RELEASE</spring.version>  
  16.         <cxf.version>3.0.0</cxf.version>  
  17.         <jackson.version>2.4.1</jackson.version>  
  18.     </properties>  
  19.   
  20.     <dependencies>  
  21.         <!-- Spring -->  
  22.         <dependency>  
  23.             <groupId>org.springframework</groupId>  
  24.             <artifactId>spring-web</artifactId>  
  25.             <version>${spring.version}</version>  
  26.         </dependency>  
  27.         <!-- CXF -->  
  28.         <dependency>  
  29.             <groupId>org.apache.cxf</groupId>  
  30.             <artifactId>cxf-rt-frontend-jaxrs</artifactId>  
  31.             <version>${cxf.version}</version>  
  32.         </dependency>  
  33.         <!-- Jackson -->  
  34.         <dependency>  
  35.             <groupId>com.fasterxml.jackson.jaxrs</groupId>  
  36.             <artifactId>jackson-jaxrs-json-provider</artifactId>  
  37.             <version>${jackson.version}</version>  
  38.         </dependency>  
  39.     </dependencies>  
  40.   
  41. </project>  
这里仅依赖 Spring Web 模块(无需 MVC 模块),此外就是 CXF 与 Jackson 了。

第二步:配置 web.xml

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns="http://java.sun.com/xml/ns/javaee"  
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
  5.          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
  6.          version="3.0">  
  7.   
  8.     <!-- Spring -->  
  9.     <context-param>  
  10.         <param-name>contextConfigLocation</param-name>  
  11.         <param-value>classpath:spring.xml</param-value>  
  12.     </context-param>  
  13.     <listener>  
  14.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  15.     </listener>  
  16.   
  17.     <!-- CXF -->  
  18.     <servlet>  
  19.         <servlet-name>cxf</servlet-name>  
  20.         <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>  
  21.     </servlet>  
  22.     <servlet-mapping>  
  23.         <servlet-name>cxf</servlet-name>  
  24.         <url-pattern>/ws/*</url-pattern>  
  25.     </servlet-mapping>  
  26.   
  27. </web-app>  
使用 Spring 提供的 ContextLoaderListener 去加载 Spring 配置文件 spring.xml;使用 CXF 提供的 CXFServlet 去处理前缀为 /ws/ 的 REST 请求。
第三步:将接口的实现类发布 SpringBean
[java]  view plain  copy
  1. package demo.ws.rest_spring_cxf;  
  2.   
  3. import org.springframework.stereotype.Component;  
  4.   
  5. @Component  
  6. public class ProductServiceImpl implements ProductService {  
  7.     ...  
  8. }  
使用 Spring 提供的 @Component 注解,将 ProductServiceImpl 发布为 Spring Bean,交给 Spring IOC 容器管理,无需再进行 Spring XML 配置。
第四步:配置 Spring
以下是 spring.xml 的配置:
[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xmlns:context="http://www.springframework.org/schema/context"  
  5.        xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
  7.        http://www.springframework.org/schema/context  
  8.        http://www.springframework.org/schema/context/spring-context-4.0.xsd">  
  9.   
  10.     <context:component-scan base-package="demo.ws"/>  
  11.   
  12.     <import resource="spring-cxf.xml"/>  
  13.   
  14. </beans>  
在以上配置中扫描 demo.ws 这个基础包路径,Spring 可访问该包中的所有 Spring Bean,比如,上面使用 @Component 注解发布的 ProductServiceImpl。此外,加载了另一个配置文件 spring-cxf.xml,其中包括了关于 CXF 的相关配置。
以下是 spring-cxf.xml 的配置:
[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xmlns:jaxrs="http://cxf.apache.org/jaxrs"  
  5.        xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
  7.        http://cxf.apache.org/jaxrs  
  8.        http://cxf.apache.org/schemas/jaxrs.xsd">  
  9.   
  10.     <jaxrs:server address="/rest">  
  11.         <jaxrs:serviceBeans>  
  12.             <ref bean="productServiceImpl"/>  
  13.         </jaxrs:serviceBeans>  
  14.         <jaxrs:providers>  
  15.             <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>  
  16.         </jaxrs:providers>  
  17.     </jaxrs:server>  
  18.   
  19. </beans>  
使用 CXF 提供的 Spring 命名空间来配置 Service Bean(即上文提到的 Resource Class)与 Provider。注意,这里配置了一个 address 属性为“/rest”,表示 REST 请求的相对路径,与 web.xml 中配置的“/ws/*”结合起来,最终的 REST 请求根路径是“/ws/rest”,在 ProductService 接口方法上 @Path 注解所配置的路径只是一个相对路径。
第五步:调用 REST 服务
[html]  view plain  copy
  1. <html>  
  2. <head>  
  3.     <meta charset="UTF-8">  
  4.     <title>Demo</title>  
  5.     <link href="http://cdn.bootcss.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">  
  6. </head>  
  7. <body>  
  8.   
  9. <div class="container">  
  10.     <div class="page-header">  
  11.         <h1>Product</h1>  
  12.     </div>  
  13.     <div class="panel panel-default">  
  14.         <div class="panel-heading">Product List</div>  
  15.         <div class="panel-body">  
  16.             <div id="product"></div>  
  17.         </div>  
  18.     </div>  
  19. </div>  
  20.   
  21. <script src="http://cdn.bootcss.com/jquery/2.1.1/jquery.min.js"></script>  
  22. <script src="http://cdn.bootcss.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>  
  23. <script src="http://cdn.bootcss.com/handlebars.js/1.3.0/handlebars.min.js"></script>  
  24.   
  25. <script type="text/x-handlebars-template" id="product_table_template">  
  26.     {{#if data}}  
  27.         <table class="table table-hover" id="product_table">  
  28.             <thead>  
  29.                 <tr>  
  30.                     <th>ID</th>  
  31.                     <th>Product Name</th>  
  32.                     <th>Price</th>  
  33.                 </tr>  
  34.             </thead>  
  35.             <tbody>  
  36.                 {{#data}}  
  37.                     <tr data-id="{{id}}" data-name="{{name}}">  
  38.                         <td>{{id}}</td>  
  39.                         <td>{{name}}</td>  
  40.                         <td>{{price}}</td>  
  41.                     </tr>  
  42.                 {{/data}}  
  43.             </tbody>  
  44.         </table>  
  45.     {{else}}  
  46.         <div class="alert alert-warning">Can not find any data!</div>  
  47.     {{/if}}  
  48. </script>  
  49.   
  50. <script>  
  51.     $(function() {  
  52.         $.ajax({  
  53.             type: 'get',  
  54.             url: 'http://localhost:8080/ws/rest/products',  
  55.             dataType: 'json',  
  56.             success: function(data) {  
  57.                 var template = $("#product_table_template").html();  
  58.                 var render = Handlebars.compile(template);  
  59.                 var html = render({  
  60.                     data: data  
  61.                 });  
  62.                 $('#product').html(html);  
  63.             }  
  64.         });  
  65.     });  
  66. </script>  
  67.   
  68. </body>  
  69. </html>  
使用一个简单的 HTML 页面来调用 REST 服务,也就是说,前端发送 AJAX 请求来调用后端发布的 REST 服务。这里使用了 jQuery、Bootstrap、Handlebars.js 等技术。

3. 关于 AJAX 的跨域问题

如果服务端部署在 foo.com 域名下,而客户端部署在 bar.com 域名下,此时从 bar.com 发出一个 AJAX 的 REST 请求到 foo.com,就会出现报错:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
要想解决以上这个 AJAX 跨域问题,有以下两种解决方案:
方案一:使用 JSONP 解决 AJAX 跨域问题
JSONP 的全称是 JSON with Padding,实际上是在需要返回的 JSON 数据外,用一个 JS 函数进行封装。
可以这样来理解,服务器返回一个 JS 函数,参数是一个 JSON 数据,例如:callback({您的 JSON 数据}),虽然 AJAX 不能跨域访问,但 JS 脚本是可以跨域执行的,因此客户端将执行这个 callback 函数,并获取其中的 JSON 数据。
如果需要返回的 JSON 数据是:
[plain]  view plain  copy
  1. {"id":2,"name":"ipad mini","price":2500},{"id":1,"name":"iphone 5s","price":5000}  
那么对应的 JSONP 格式是:
[plain]  view plain  copy
  1. callback([{"id":2,"name":"ipad mini","price":2500},{"id":1,"name":"iphone 5s","price":5000}]);  
CXF 已经提供了对 JSONP 的支持,只需要通过简单的配置即可实现。
首先,添加 Maven 依赖:
[html]  view plain  copy
  1. <dependency>  
  2.     <groupId>org.apache.cxf</groupId>  
  3.     <artifactId>cxf-rt-rs-extension-providers</artifactId>  
  4.     <version>${cxf.version}</version>  
  5. </dependency>  
然后,添加 CXF 配置:
[html]  view plain  copy
  1. <jaxrs:server address="/rest">  
  2.     <jaxrs:serviceBeans>  
  3.         <ref bean="productServiceImpl"/>  
  4.     </jaxrs:serviceBeans>  
  5.     <jaxrs:providers>  
  6.         <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>  
  7.         <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPreStreamInterceptor"/>  
  8.     </jaxrs:providers>  
  9.     <jaxrs:inInterceptors>  
  10.         <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpInInterceptor"/>  
  11.     </jaxrs:inInterceptors>  
  12.     <jaxrs:outInterceptors>  
  13.         <bean class="org.apache.cxf.jaxrs.provider.jsonp.JsonpPostStreamInterceptor"/>  
  14.     </jaxrs:outInterceptors>  
  15. </jaxrs:server>  
注意:JsonpPreStreamInterceptor 一定要放在 <jaxrs:providers> 中,而不是 <jaxrs:inInterceptors> 中,这也许是 CXF 的一个 Bug,可以点击以下链接查看具体原因:

http://cxf.547215.n5.nabble.com/JSONP-is-not-works-td5739858.html

最后,使用 jQuery 发送基于 JSONP 的 AJAX 请求:

[javascript]  view plain  copy
  1. $.ajax({  
  2.     type: 'get',  
  3.     url: 'http://localhost:8080/ws/rest/products',  
  4.     dataType: 'jsonp',  
  5.     jsonp: '_jsonp',  
  6.     jsonpCallback: 'callback',  
  7.     success: function(data) {  
  8.         var template = $("#product_table_template").html();  
  9.         var render = Handlebars.compile(template);  
  10.         var html = render({  
  11.             data: data  
  12.         });  
  13.         $('#product').html(html);  
  14.     }  
  15. });  
以上代码中有三个选项需要加以说明:
dataType:必须为“jsonp”,表示返回的数据类型为 JSONP 格式
jsonp:表示 URL 中 JSONP 回调函数的参数名,CXF 默认接收的参数名是“_jsonp”,也可以在 JsonpInInterceptor 中配置
jsonpCallback:表示回调函数的名称,若未指定,则由 jQuery 自动生成
方案二:使用 CORS 解决 AJAX 跨域问题
CORS 的全称是 Cross-Origin Resource Sharing(跨域资源共享),它是 W3C 提出的一个 AJAX 跨域访问规范,可以从以下地址了解此规范:
http://www.w3.org/TR/cors/
相比 JSONP 而言,CORS 更为强大,因为它弥补了 JSONP 只能处理 GET 请求的局限性,但是只有较为先进的浏览器才能全面支持 CORS。
CXF 同样也提供了对 CORS 的支持,通过简单的配置就能实现。
首先,添加 Maven 依赖:
[html]  view plain  copy
  1. <dependency>  
  2.     <groupId>org.apache.cxf</groupId>  
  3.     <artifactId>cxf-rt-rs-security-cors</artifactId>  
  4.     <version>${cxf.version}</version>  
  5. </dependency>  
然后,添加 CXF 配置:
[html]  view plain  copy
  1. <jaxrs:server address="/rest">  
  2.     <jaxrs:serviceBeans>  
  3.         <ref bean="productServiceImpl"/>  
  4.     </jaxrs:serviceBeans>  
  5.     <jaxrs:providers>  
  6.         <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>  
  7.         <bean class="org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter">  
  8.             <property name="allowOrigins" value="http://localhost"/>  
  9.         </bean>  
  10.     </jaxrs:providers>  
  11. </jaxrs:server>  
在 CrossOriginResourceSharingFilter 中配置 allowOrigins 属性,将其设置为客户端的域名,示例中为“http://localhost”,需根据实际情况进行设置。
最后,使用 jQuery 发送 AJAX 请求:
就像在相同域名下访问一样,无需做任何配置。
注意:在 IE8 中使用 jQuery 发送 AJAX 请求时,需要配置 $.support.cors = true,才能开启 CORS 特性。

4. 总结

本文让您学会了如何使用 CXF 发布 REST 服务,可以独立使用 CXF,也可以与 Spring 集成。此外,CXF 也提供了一些解决方案,用于实现跨域 AJAX 请求,比如:JSONP 或 CORS。CXF 3.0 以全面支持 JAX-RS 2.0 规范,有很多实用的功能需要您进一步学习,可以点击以下地址:
http://cxf.apache.org/docs/jax-rs.html

源码地址:http://git.oschina.net/huangyong/cxf_demo

原文地址:https://my.oschina.net/huangyong/blog/294324

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值