java_java 利用JAX-RS快速开发RESTful 服务

JAX-RS(Java API for RESTful Web Services)同样也是JSR的一部分,详细规范定义见 https://jcp.org/en/jsr/detail?id=311 。从JAVA EE 6开始,已经内置了该技术,跟.NET中的RESTful WCF类似,只要在常规方法上使用一些简单的注解,就可以对外暴露成RESTful 服务.

注:本文运行环境为Jboss EAP 6.x ,其它容器特别是tomcat,并未自带JAX-RS依赖的jar包,如果采用tomcat,需要自己导入这些相关依赖jar包。

一、最基础的HelloWorld (无参数)

 1 @Path("/")
 2 public class MyService {
 3
 4     final String XMLNS_NAMESPACE = "http://yjmyzz.cnblogs.com/rest/service";
 5     final String ROOT_NODE = "root";
 6
 7     @GET
 8     @Path("/json/hello")
 9     @Produces(MediaType.APPLICATION_JSON)
10     public JAXBElement<String> getHelloWorldJSON() {
11         JAXBElement<String> result = new JAXBElement<String>(new QName("",
12                 ROOT_NODE), String.class, sayHelloWorld());
13         return result;
14     }
15 。。。
16 }

getHelloWorldJSON

1     private String sayHelloWorld() {
2         return "Hello JAX-RS!";
3     }

sayHelloWorld

解释: @GET 表示该服务可以直接在浏览器地址栏里访问(对应HTTP请求中的GET方法)

@Path 这里用了二次,第一次是在Class上,相当服务的基地址;第二次是在方法上,表示具体某个服务方法的URL.

此外web.xml中,还需要增加以下内容:

1     <servlet-mapping>
2         <servlet-name>javax.ws.rs.core.Application</servlet-name>
3         <url-pattern>/rest/*</url-pattern>
4     </servlet-mapping>

web.xml

@Produces 表示返回消息的格式,配合MediaType.APPLICATION_JSON说明,本方法返回Json字符串格式

以上几项综合起来,最终可以用类似 http://localhost:8080/jboss-as-helloworld-rs/rest/json/hello 的地址来访问,返回的结果为:

JAXBElement<T> 类似一个包装器(Wrapper),用于将返回对象做下json/xml格式的包装,比如上图中的{root:}就是它的功劳,感兴趣的朋友,可以把返回类似改成最原始的String对比下差异.

如果需要返回XML格式,只需要把@Produces后面的MediaType.APPLICATION_JSON换成MediaType.APPLICATION_XML就行了,即:

1     @GET
2     @Path("/xml/hello")
3     @Produces(MediaType.APPLICATION_XML)
4     public JAXBElement<String> getHelloWorldXML() {
5         JAXBElement<String> result = new JAXBElement<String>(new QName(
6                 XMLNS_NAMESPACE, ROOT_NODE), String.class, sayHelloWorld());
7         return result;
8     }

getHelloWorldXML

效果如下:

二、带一个基本类型参数的HelloWorld

 1     @GET
 2     @Path("/json/hi/{name}")
 3     @Produces(MediaType.APPLICATION_JSON)
 4     public JAXBElement<String> getHelloToNameJSON(@PathParam("name") String name) {
 5         JAXBElement<String> result = new JAXBElement<String>(new QName("",
 6                 ROOT_NODE), String.class, sayHelloToName(name));
 7         return result;
 8     }
 9
10     @GET
11     @Path("/xml/hi/{name}")
12     @Produces(MediaType.APPLICATION_XML)
13     public JAXBElement<String> getHelloToNameXML(@PathParam("name") String name) {
14         JAXBElement<String> result = new JAXBElement<String>(new QName(
15                 XMLNS_NAMESPACE, ROOT_NODE), String.class, sayHelloToName(name));
16         return result;
17     }

getHelloToNameJSON & getHelloToNameXML

1     private String sayHelloToName(String name) {
2         return "Hello " + name + ",welcome to the world of JAX-RS!";
3     }

sayHelloToName

对比一下刚才的代码,有二个变化:

2.1 @Path上多了一个{name}的部分,表示url中,将允许以 /json/hi/xxx 的访问URL响应请求

2.2 方法参数前有一个 @PathParam("name") 的注解,表示如果按/json/hi/xxx的方式访问,则url中xxx的部分,会被映射到name这个参数上(说得更通俗点,就是通过解析url最后的xxx部分直接把name传进来)

运行效果如下:

三、返回结果为实体Bean

实际应用中,web服务返回的结果,除了基本类型,还有可能是实体Bean,先来定义一个User类

 1 package yjmyzz.service.model;
 2
 3 import javax.xml.bind.annotation.XmlAttribute;
 4 import javax.xml.bind.annotation.XmlElement;
 5 import javax.xml.bind.annotation.XmlRootElement;
 6
 7 @XmlRootElement(name = "user")
 8 public class User {
 9
10     String username;
11     String password;
12     int pin;
13
14     @XmlElement
15     public String getUsername() {
16         return username;
17     }
18
19     public void setUsername(String username) {
20         this.username = username;
21     }
22
23     @XmlElement
24     public String getPassword() {
25         return password;
26     }
27
28     public void setPassword(String password) {
29         this.password = password;
30     }
31
32     @XmlAttribute
33     public int getPin() {
34         return pin;
35     }
36
37     public void setPin(int pin) {
38         this.pin = pin;
39     }
40
41 }

User

代码中加的一堆@XML开头的注解,是为了在传输过程中,如果需要以XML格式传输,这些注解可以帮助完成 XML <-> Obj的转换(详情可参见JAXB相关内容

 1     @GET
 2     @Path("/xml/user")
 3     @Produces(MediaType.APPLICATION_XML)
 4     public User getUserInXML() {
 5         return getUser("unknown");
 6     }
 7
 8     @GET
 9     @Path("/json/user")
10     @Produces(MediaType.APPLICATION_JSON)
11     public User getUserInJSON() {
12         return getUser("unknown");
13     }

getUserInXML & getUserInJSON

注意:这里我们没有使用JAXBElement<T>对结果进行包装,辅助方法getUser()代码如下

1     private User getUser(String username) {
2         User user = new User();
3         user.setUsername(username);
4         String pwd = new Long(Math.round(Math.random() * 100000)).toString();
5         user.setPassword(pwd);
6         int pin = (int) (Math.random() * 1000);
7         user.setPin(pin);
8         return user;
9     }

getUser

运行结果如下:

当然,也可以给方法增加参数,先从最基本类型的String参数耍起:

 1     @GET
 2     @Path("/xml/userByName/{name}")
 3     @Produces(MediaType.APPLICATION_XML)
 4     public User getUserInXML(@PathParam("name") String username) {
 5         return getUser(username);
 6     }
 7
 8     @GET
 9     @Path("/json/userByName/{name}")
10     @Produces(MediaType.APPLICATION_JSON)
11     public User getUserInJSON(@PathParam("name") String username) {
12         return getUser(username);
13     }

getUserWithStringParameter

运行结果如下:

如果加上包装器JAXBElement<User>后,返回结果会变成什么样呢? 我也很好奇,so, 嘿-喂狗!

 1     @GET
 2     @Path("/xml/userByName2/{name}")
 3     @Produces(MediaType.APPLICATION_XML)
 4     public JAXBElement<User> getUserInXML2(@PathParam("name") String username) {
 5         JAXBElement<User> result = new JAXBElement<User>(new QName(
 6                 XMLNS_NAMESPACE, ROOT_NODE), User.class, getUser(username));
 7         return result;
 8     }
 9
10     @GET
11     @Path("/json/userByName2/{name}")
12     @Produces(MediaType.APPLICATION_JSON)
13     public JAXBElement<User> getUserInJSON2(@PathParam("name") String username) {
14         JAXBElement<User> result = new JAXBElement<User>(new QName("",
15                 ROOT_NODE), User.class, getUser(username));
16         return result;
17     }

getUserWithWrapper

结果如下:

个中差异,请自行体会

四、单个String参数的实体Bean注入

web服务中方法的传入参数,不可能都是基础类型,同样也可以是实体Bean

 1     @GET
 2     @Path("/xml/product/{name}")
 3     @Produces(MediaType.APPLICATION_XML)
 4     public Product getProductXML(@PathParam("name") Product product) {
 5         return getProductFromServer(product);
 6     }
 7
 8     @GET
 9     @Path("/xml/product2/{name}")
10     @Produces(MediaType.APPLICATION_XML)
11     public JAXBElement<Product> getProductXML2(
12             @PathParam("name") Product product) {
13         JAXBElement<Product> result = new JAXBElement<Product>(new QName(
14                 XMLNS_NAMESPACE, ROOT_NODE), Product.class,
15                 getProductFromServer(product));
16         return result;
17     }

getProductXML & getProductXML2

这里我们用到了一个新的Bean :Product类,代码如下:

 1 package yjmyzz.service.model;
 2
 3 import javax.xml.bind.annotation.XmlElement;
 4 import javax.xml.bind.annotation.XmlRootElement;
 5
 6 @XmlRootElement(name = "product")
 7 public class Product {
 8
 9     public Product() {
10         System.out.println("construct: Product() is called!");
11     }
12
13     public Product(String name) {
14         System.out.println("construct: Product(String name) is called!");
15         this.productName = name;
16     }
17
18     @XmlElement
19     public String getProductName() {
20         return productName;
21     }
22
23     public void setProductName(String productName) {
24         this.productName = productName;
25     }
26
27     private String productName;
28
29 }

Product Bean

先提个问题:从@Path上看,我们希望用类似 /xml/product/xxx 来请求该web方法,但是方法中的参数是一个Bean实例,而url中的xxx部分只是一个字符串,一个字符串如何被映射成一个Bean实例呢?

关键在于Product中的构造函数:Product(String name) ,方法getProductXML(@PathParam("name") Product product) 被请求时,url中的xxx部分会先映射成参数name,然后name又会被传入到构造函数Product(String name)中,于是一个崭新的Product对象诞生了!(可以通过eclipse Console的输出来印证Product(String name)是否被调用)

运行结果:

eclipse的控制台里,也可以看到构造器,确实被调用了

除了利用“带参数的构造器”来实现“url参数->Bean实例”的注入,JAX-RS机制还允许使用Bean自身提供的static valueOf()方法来实现注入,再来看一下示例:

从Product派生一个子类Book:

 1 package yjmyzz.service.model;
 2
 3 import java.util.Calendar;
 4 import java.util.Date;
 5
 6 import javax.xml.bind.annotation.XmlElement;
 7 import javax.xml.bind.annotation.XmlRootElement;
 8
 9 @XmlRootElement(name = "book")
10 public class Book extends Product {
11
12     private String isbn;
13
14     private String author;
15
16     private Date publishDate;
17
18     public Book() {
19         System.out.println("construct: Book() is called!");
20     }
21
22     public static Book valueOf(String isbn) {
23         System.out.println("Book: valueOf(String isbn) is called!");
24
25         Book book = new Book();
26         book.setISBN(isbn);
27         book.setProductName("Java编程思想(第4版)");
28
29         book.setAuthor("[美]埃克尔");
30
31         Calendar calendar = Calendar.getInstance();
32         calendar.set(2007, 6, 1);
33         book.setPublishDate(calendar.getTime());
34         return book;
35     }
36
37     @XmlElement
38     public String getISBN() {
39         return isbn;
40     }
41
42     public void setISBN(String isbn) {
43         this.isbn = isbn;
44     }
45
46     @XmlElement
47     public String getAuthor() {
48         return author;
49     }
50
51     public void setAuthor(String author) {
52         this.author = author;
53     }
54
55     @XmlElement
56     public Date getPublishDate() {
57         return publishDate;
58     }
59
60     public void setPublishDate(Date publishDate) {
61         this.publishDate = publishDate;
62     }
63
64 }

Book

再定义一个新方法:

1     @GET
2     @Path("/xml/book/{ISBN}")
3     @Produces(MediaType.APPLICATION_XML)
4     public Book getBookXML(@PathParam("ISBN") Book book) {
5         return getBookFromServer(book);
6     }

getBookXML

里面有一个辅助方法,代码如下:

1     private Book getBookFromServer(Book book) {
2         book.setProductName(book.getProductName() + " from server!");
3         return book;
4     }

getBookFromServer

运行效果如下:

eclipse的控制台信息如下:

五、POST方式的Bean实例参数注入

前面提到的都是GET方式的web服务方法,GET方式能传输的数据大小有限,对于较大的参数数据,直接POST的场景更常见,下面是一个示例:

 1     @POST
 2     @Path("/json/add-book")
 3     @Produces(MediaType.APPLICATION_JSON)
 4     @Consumes(MediaType.APPLICATION_JSON)
 5     public Book addBookJSON(Book book) {
 6         return getBookFromServer(book);
 7     }
 8
 9
10     @POST
11     @Path("/xml/add-book")
12     @Produces(MediaType.APPLICATION_XML)
13     @Consumes(MediaType.APPLICATION_XML)
14     public Book addBookXML(Book book) {
15         return getBookFromServer(book);
16     }

add-book

解释一下:

又遇到了一个新的注解:@Consumes(MediaType.APPLICATION_JSON) 它表示传入的Book实例是json格式。

我们用jQuery以Ajax方式调用这个服务演示一下:

 1         function testJsonAddBook(){
 2             $(document).ready(function(){
 3                 $.ajax({
 4                     type:"POST",
 5                     url:"rest/json/add-book",
 6                     dataType: "json",
 7                     contentType: ‘application/json‘,
 8                     data:JSON.stringify({"productName":"Java编程思想(第4版)","isbn":"9787111213826","author":"[美]埃克尔","publishDate":1183270161125}),
 9                     success: function(data, textStatus, jqXHR){
10                         alert("以下是从服务器返回的Book对象:\n\n" + JSON.stringify(data));
11                     },
12                     error: function(jqXHR, textStatus, errorThrown){
13                         alert(‘error: ‘ + textStatus);
14                     }
15                 });
16             })
17         }
18
19
20         function testXmlAddBook(){
21             $(document).ready(function(){
22                 $.ajax({
23                     type:"POST",
24                     url:"rest/xml/add-book",
25                     dataType: "xml",
26                     contentType: ‘application/xml‘,
27                     data:"<book><productName>Java编程思想(第4版) from server!</productName><author>[美]埃克尔</author><ISBN>9787111213826</ISBN><publishDate>2007-07-01T13:33:30.782+08:00</publishDate></book>",
28                     success: function(data, textStatus, jqXHR){
29                         alert("以下是从服务器返回的Book对象:\n\n" + data.documentElement.outerHTML);
30                     },
31                     error: function(jqXHR, textStatus, errorThrown){
32                         alert(‘error: ‘ + textStatus);
33                     }
34                 });
35             })
36         }

testJsonAddBook & testXmlAddBook

运行效果如下:

如果断点调试ajax,可以看到传过来的json串已经映射成Bean实例了

时间: 2024-10-10 17:57:38

java_java 利用JAX-RS快速开发RESTful 服务的相关文章

Web API应用架构在Winform混合框架中的应用(4)--利用代码生成工具快速开发整套应用

前面几篇介绍了Web API的基础信息,以及如何基于混合框架的方式在WInform界面里面整合了Web API的接入方式,虽然我们看似调用过程比较复杂,但是基于整个框架的支持和考虑,我们提供了代码生成工具的整合,使得开发整套应用是非常方便和高效的.本文主要介绍如何利用代码生成工具Database2Sharp,如何迅速生成基于Web API的Winform应用. 1.代码生成工具的功能介绍 代码生成工具Database2Sharp,是我为整个开发过程开发的一款核心软件,已经走过了10个年头,随着开

在ASP.NET Core中使用Apworks快速开发数据服务

不少关注我博客的朋友都知道我在2009年左右开发过一个名为Apworks的企业级应用程序开发框架,旨在为分布式企业系统软件开发提供面向领域驱动(DDD)的框架级别的解决方案,并对多种系统架构风格提供支持.这个框架的开发和维护我坚持了很久,一直到2015年,我都一直在不停地重构这个项目.目前这个项目在Github上也得到了将近260的推荐数,很多对技术感兴趣的朋友也一直与我保持着联系和交流,甚至还有爱好者自发组成了技术讨论群,专门讨论分享Apworks框架. 然而,随着软件开发技术和.NET的发展

LBPL--基于Asp.net、 quartz.net 快速开发定时服务的插件化项目

LBPL 这一个基于Asp.net. quartz.net 快速开发定时服务的插件化项目 由于在实际项目开发中需要做定时服务的操作,大体上可以理解为:需要动态化监控定时任务的调度系统. 为了实现快速开发.简单明了完成一个服务,不再繁琐的进行各种代码耦合度强的操作,设计了一个以暂时以quartz.net 为服务载体,插件化开发的模式. github地址:https://github.com/linbin524/LBPL 由于工作比较忙,暂时更新部分,后续会将相关的开发文档更新. 主要开发流程 1.

使用Jersey开发RESTful服务

REST 2000 年由 Roy Fielding 在博士论文中提出,他是 HTTP 规范 1.0 和 1.1 版的首席作者之一. REST 中最重要的概念是资源(resources) ,使用全球 ID(通常使用 URI)标识.客户端应用程序使用 HTTP 方法(GET/ POST/ PUT/ DELETE )操作资源或资源集.RESTful Web 服务是使用 HTTP 和 REST 原理实现的 Web 服务.通常,RESTful            Web 服务应该定义以下方面: Web

利用MVVM设计快速开发个人中心、设置等模块

我们在做iOS开发过程中,静态页面的开发比开发动态页面更让我们开发者抓狂.因为动态页面通常是一个页面一种cell样式,作为开发者只需要专注于定制好一种样式之后,就可以使用数据填充出较好的界面.而静态cell,则可能因为一个页面有多种cell样式,而且很有可能不是标准的cell样式,需要我们自定义,因此容易写出来的代码容易臃肿和重复.可读性差.这不符合开发者的代码优化原则(不主动重复).我时常会因为开发这种页面抓狂,因为会纠结到底怎么写会让代码可读性好点,写重复代码的机会少点.本文就是我做过的几个

基于dubbo快速发布restful服务

本文使用官方项目示例,基于dubbox发布restful服务.jdk,eclipse.maven神马环境的就不讲了,自行baidu之. dubbox下载.编译 下载地址:https://github.com/dangdangdotcom/dubbox下载方式可以用git下载,也可以打包下载.下载之后,用maven导入(eclipse->import->Maven).dubbo项目结构: 使用maven打包,版本2.8.4. zookeeper安装 本文使用的是单节点的注册中心,在实际项目中需要

利用百度AI快速开发出一款“问答机器人”并接入小程序

先看实现效果: 利用百度UNIT预置的智能问答技能和微信小程序,实现语音问答机器人.这里主要介绍小程序功能开发实现过程,分享主要功能实现的子程序模块,都是干货! 想了解UNIT预置技能调用,请参看我之前的帖子:<UNIT搭建机器人助理> https://ai.baidu.com/forum/topic/show/953021 想了解微信小程序的开发过程,请参看我之前的帖子:<UNIT接入小程序>https://ai.baidu.com/forum/topic/show/953022

虚拟机部署度量快速开发平台服务端出现ora-27101错误的一个解决方法

最近在使用hyper-v虚拟机部署系统总出现的一个数据库错误,部署情况如下:1.把之前现有的一个服务端环境的hyper-v停止,拷贝出去vhd文件2.建立新的虚拟机,选择拷贝出去的文件3.启动新的虚拟机,并更改虚拟机的计算机名称,修改oracle数据库的监听和服务名.4.重启oracle的实例和监听.5.连接到数据库,发现报告以下错误:Error:ORA-01034:Oracle not availableORA-27101:shared memory realm dose not exists

开发者如何利用工具快速开发出完美APP

文|移动互联网李建华 微信:ydhlwdyq 传统的个人开发者,要想开发出一个完美的APP要 经过以下几个过程:搭建开发环境,写代码,写统计系统,开发即将完成后,要购买服务器,然后把程序布置到服务器上进行测试,接着是对APP做手机适配性测 试,需要借很多手机进行测试,测试完毕后,上线,和很多渠道商合作,然后发布上百个渠道等,后续还可能涉及到很多复杂的运营工作,如要赚钱,同时还面临着 一系列的山寨盗版问题,还要面临着存储速度.还要找广告商等等一系列的问题. 根据这些工作量,一个人要想开发出完美的A