REST(Representational State Transfer)是 Roy Fielding 提出的一个描述互联系统架构风格的名词。为什么称为 REST?Web 本质上由各种各样的资源组成,资源由 URI 唯一标识。浏览器(或者任何其它类似于浏览器的应用程序)将展示出该资源的一种表现方式,或者一种表现状态。如果用户在该页面中定向到指向其它资源的链接,则将访问该资源,并表现出它的状态。这意味着客户端应用程序随着每个资源表现状态的不同而发生状态转移,也即所谓 REST。
凡符合REST原则的框架成为RESTful框架。
为了方便起见,本文使用了RESTeasy作为REST的3PP,和JBOSS7集成。这样不用打很多3pp包了,JBOSS自带。
1. 建立项目
用Maven命令创建基本项目:
mvn archetype:generate -DgroupId=com.edi.test -DartifactId=resteasy-test -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
然后再手动创建webapp目录,最终目录结构如下: (请无视eclipse自己创建的.setting文件夹)
然后打开根目录下的pom.xml,修改如下
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.edison.test</groupId>
<artifactId>edi-test</artifactId>
<version>1.0</version>
</parent>
<groupId>com.edi.test</groupId>
<artifactId>resteasy-test</artifactId>
<version>1.0-SNAPSHOT</version>
<name>resteasy-test</name>
<url>http://maven.apache.org</url>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<version>2.3.0.GA</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>2.3.0.GA</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>2.3.0.GA</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>resteasy-test</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 定义交互数据类型
首先定义交换的XML格式如下:
<user>
<id>1</id>
<name>test</name>
</user>
再利用工具生成该XML的XSD文件。可以用这个在线的:http://www.freeformatter.com/xsd-generator.html
生成xsd如下:
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="user" type="userType" />
<xsd:complexType name="userType">
<xsd:sequence>
<xsd:element name="id" type="xsd:int" />
<xsd:element name="name" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
将该xsd保存至resteasy-test/src/main/resources目录,然后利用jdk的xjc命令利用JAXB根据xsd生成java类
xjc test.xsd -p com.edi.test -d ../java
-p 参数添加包名
然后在java目录下会生成对应java 文件如下:
3. 完成RESTful化
UserType是XML数据对应的Java类,但是服务器端一般有自己的pojo来封装对象。所以我们在服务器端创建自己的封装类。
package com.edi.test;
public class MyUser {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyUser [id=" + id + ", name=" + name + "]";
}
}
然后实现WebService的处理类,这里我们直接用SLSB
package com.edi.test;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.Stateless;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
@Stateless
@Path("/users")
public class UserHandler {
private static Map<Integer, MyUser> map = new ConcurrentHashMap<Integer, MyUser>();
@POST
@Path("/create")
@Consumes({ "application/xml" })
public Response addUser(UserType user)
{
MyUser myUser = new MyUser();
myUser.setId(user.getId());
myUser.setName(user.getName());
System.out.println(myUser + " is created.");
map.put(myUser.getId(), myUser);
return Response.created(URI.create("/users/" + myUser.getId())).build();
}
@GET
@Path("/{id}")
@Produces("application/xml")
public UserType getUser(@PathParam("id") int id)
{
MyUser user = map.get(Integer.valueOf(id));
if(user==null)
throw new WebApplicationException(Response.Status.NOT_FOUND);
UserType u = new UserType();
u.setId(user.getId());
u.setName(user.getName());
return u;
}
}
@POST和@GET 是对于http中POST和GET方法,resteasy 会自动将@Path里面对应路径的请求转交给给方法处理,并且用JAXB将请求里带的xml解析后转成对应的Java类(前面xjc生成的)作为参数传进来。
4. 部署
这里部署时有三种方法:
a) 继承application类加上@ApplicationPath("path")注解
创建一个空类继承java.ws.rs.Application,并且标上注解@ApplicationPath() 路径为你想要该Rest请求所listen的路径
package com.edi.test;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/")
public class MyApplication extends Application {
}
同样创建一个空类继承Application
package com.edi.test;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
public class MyApplication extends Application {
}
创建resteasy-test/src/main/webapp/WEB-INF/web.xml ,里面指明listen地址替代@Applicationpath 注解
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Restful Web Service for NBI </display-name>
<context-param>
<param-name>resteasy.jndi.resources</param-name>
<param-value>java:app/resteasy-test/UserHandler</param-value>
</context-param>
<servlet-mapping>
<servlet-name>com.edi.test.MyApplication</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
java:app/resteasy-test/UserHandler 是我们的WebService类的Portable JNDIName
c) 只直接用web.xml
创建resteasy-test/src/main/webapp/WEB-INF/web.xml 如下
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Restful Web Service for NBI </display-name>
<context-param>
<param-name>resteasy.jndi.resources</param-name>
<param-value>java:app/resteasy-test/UserHandler</param-value>
</context-param>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
5. 测试
用maven编译打包后放入JBoss7,启动JBoss后,用UnitTest来测试
package com.edi.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.junit.Assert;
import org.junit.Test;
public class TestUserHandler {
public static final String TARGET_POST_URL = "http://127.0.0.1:8080/resteasy-test/users/create";
public static final String TARGET_GET_URL = "http://127.0.0.1:8080/resteasy-test/users/1";
@Test
public void testCreate() throws IOException {
URL url = new java.net.URL(TARGET_POST_URL);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/xml");
conn.setDoOutput(true);
conn.setInstanceFollowRedirects(false);
conn.setConnectTimeout(1000);
String userXML = "<user><id>1</id><name>test</name></user>";
OutputStream os = conn.getOutputStream();
os.write(userXML.getBytes());
os.flush();
Assert.assertEquals(HttpURLConnection.HTTP_CREATED, conn.getResponseCode());
conn.disconnect();
}
@Test
public void testGet() throws IOException {
testCreate();
URL url = new java.net.URL(TARGET_GET_URL);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
System.out.println("Result:");
String out = null;
while((out=reader.readLine())!=null){
System.out.println(out);
}
Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
conn.disconnect();
}
}
好吧,这只是测试代码,别要求我写太认真……
当然,你也可以把该测试类放到reseteasy-test/src/test/对应路径下,然后引入嵌入式容器来做测试,比如Jetty或openejb。这里就不多叙述了。