java restful接口开发_Java基于JAX-RS开发Restful接口总结

JAX-RS常用注解:

@Path,标注资源类或者方法的相对路径

@GET,@PUT,@POST,@DELETE,标注方法是HTTP请求的类型。

@Produces,标注返回的MIME媒体类型

@Consumes,标注可接受请求的MIME媒体类型

@PathParam,@QueryParam,@HeaderParam,@CookieParam,@MatrixParam,@FormParam,

分别标注方法的参数来自于HTTP请求的不同位置,

例如

@PathParam来自于URL的路径,

@QueryParam来自于URL的查询参数,

@HeaderParam来自于HTTP请求的头信息,

@CookieParam来自于HTTP请求的Cookie。

下面结合上面的几个常用的注解来进行说明。

一、导入依赖

maven:

javax.ws.rs

javax.ws.rs-api

2.0

gradle:

compile "javax.ws.rs:javax.ws.rs-api:2.0"

二、编写测试代码

下面在类上和方法上都加了@Path注解,这是一个请求路径映射的最简配置(类和方法上的@Path路径都只是使用一个"/"斜杠表示):

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;importjavax.ws.rs.GET;importjavax.ws.rs.Path;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@GET

@Path("/")publicString testMethod() {

LOGGER.info("===TestController#test===");return "TestController#test";

}

}

三、启动web容器,在浏览器中访问 "http://localhost:8080/"

20180110231200490340.png

上面的代码中,由于在类和方法的@path注解的value值都只是一个"/"斜杠,所以直接访问"http://localhost:8080"就能将请求映射到TestController类的testMethod()方法中。

四:@Path注解的几个测试

如果在类上不使用@Path注解,而仅在方法上使用@Path注解会怎么样呢?

20180110231200493270.png

这次,将类上的@path注解去掉了,启动web容器,再次访问"http://localhost:8080/‘:

20180110231200499130.png

相反,如果仅在类上使用@Path注解,而在方法上没有使用@Path注解,这显然是没有任何意义的,因为我们使用@Path注解的目的就是将请求映射到某个方法中。

总结:在类和方法都要有@Path注解,缺一不可,否则,请求就无法映射到类的方法中。

在类上使用@Path("/class"),在方法上使用@Path("/test01")这样的方式:

@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@GET

@Path("/")publicString testMethod() {

LOGGER.info("===TestController#test===");return "TestController#test";

}

@GET

@Path("/test01")publicString test01() {

LOGGER.info("===TestController#test01===");return "TestController#test01";

}

}

启动web容器,在浏览器中访问"http://localhost:8080/class":

20180110231200501083.png

在浏览器中访问:http://localhost:8080/class/test01

20180110231200513779.png

@POST注解测试:

20180110231200526475.png

图中test02()方法上使用了@POST注解,如果在浏览器中直接访问"http://localhost:8080/class/test02",会报405:

20180110231200538194.png

因为请求方式不对,所以会返回状态码405。下面使用火狐浏览器的Poster插件来发送POST请求:

20180110231200550890.png

点击POST按钮,向服务器发送post请求,响应结果如下:

20180110231200565539.png

给test03()方法加上HttpServletRequest参数:

20180110231200577258.png

经测试,POST请求是可以发送成功的,但是request的值为null:

20180110231200589954.png

给HttpServletRequest参数加上@Context注解就好了:

20180110231200602649.png

下面测试一下HttpServletRequest的请求头:

/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@POST

@Path("/test03")publicString test03(@Context HttpServletRequest request) {if (request != null) {

Enumeration headerNames =request.getHeaderNames();while(headerNames.hasMoreElements()) {

String name=headerNames.nextElement();

String value=request.getHeader(name);

LOGGER.info(name+ " = " +value);

}

}return "TestController#test03";

}

}

使用poster插件发送请求时在请求头中传一个参数,比如name=zhangsan:

20180110231200615345.png

[17/04/23 12:46:08:142][com.zhaopin.api.test.TestController-test03] host = localhost:8080[17/04/23 12:46:08:143][com.zhaopin.api.test.TestController-test03] user-agent = Mozilla/5.0 (Windows NT 6.1; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0[17/04/23 12:46:08:144][com.zhaopin.api.test.TestController-test03] accept = */*[17/04/23 12:46:08:144][com.zhaopin.api.test.TestController-test03] accept-language = zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3[17/04/23 12:46:08:144][com.zhaopin.api.test.TestController-test03] accept-encoding =gzip, deflate

[17/04/23 12:46:08:145][com.zhaopin.api.test.TestController-test03]name =zhangsan

[17/04/23 12:46:08:145][com.zhaopin.api.test.TestController-test03] connection = keep-alive

[17/04/23 12:46:08:145][com.zhaopin.api.test.TestController-test03] content-length = 0

也可以使用request.getHeader("xxx")方法来获取请求头中的信息:

20180110231200629018.png

测试获取请求体(RequestBody):

(1)下面的test04(String name)方法有一个name参数,并且该参数没有加任何注解:

下面使用火狐浏览器的poster插件发送请求:

20180110231200652456.png

看控制台上的打印结果,说明服务器拿到了这个参数:

20180110231200669058.png

在上面使用poster插件发送请求时的Content-Type是text/xml,换成别的会怎么样呢?

20180110231200689567.png

打印结果:

20180110231200702263.png

将Content-Type换成application/json试试:

20180110231200723748.png

依然能够拿到数据:

20180110231200742303.png

如果我们使用多个参数呢?比如,下面的test05()方法有两个参数,如果启动服务器在浏览器访问的话,会响应500状态码:

20180110231200755976.png

20180110231200774531.png

说明服务器出现异常。怎么办呢?

可以使用json来解决,就是请求体传递的是一个json格式的字符串,比如:

20180110231200791133.png

然后test05(String jsonString)方法使用一个String类型的参数来接收,就像下面这样:

20180110231200807735.png

拿到了json格式的字符串,我们就可以对将这个json解析成对象了。

比如,先创建一个Person对象如下:

/*** Created by SYJ on 2017/4/23.*/

public classPerson {privateString name;privateString address;publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}publicString getAddress() {returnaddress;

}public voidsetAddress(String address) {this.address =address;

}

@OverridepublicString toString() {return "Person{" +

"name=‘" + name + ‘\‘‘ +

", address=‘" + address + ‘\‘‘ +

‘}‘;

}

}

然后使用fastjson将json格式的字符串解析成Person对象:

20180110231200821408.png

当然,也可以直接使用一个Person对象作为参数来接收json格式的数据:

20180110231200832150.png

注意在请求的时候,Content Type必须是"application/json"格式(否则会响应415,不支持的Media Type媒体类型)。

20180110231200857542.png

下面看一下,如果在上面的请求的时候如果Content Type不是application/json的情况:

20180110231200871214.png

后台打印输出的错误信息:

四月 23, 2017 3:31:40下午 org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor aroundReadFrom

严重: MessageBodyReadernot found for media type=text/xml, type=class com.zhaopin.api.test.Person, genericType=class com.zhaopin.api.test.Person.

这说明如果方法的参数是一个对象的话,要求请求的Content Type必须是"application/json",并且请求体必须json格式的数据,否则无法完成属性的映射。

再来看一个问题,如果请求的json数据中,多传了一个Person对象中没有的字段会怎么样呢?

20180110231200882934.png

响应的状态码为400:

20180110231200896606.png

意思是说,在Person类中没有找到"mail"这个属性,没有标记为忽略。什么意思呢?

我们需要在Person类上加上一个注解@JsonIgnoreProperties,并指定ignoreUnknown的值为true(默认为false),来忽略Person中没有的字段:

importcom.fasterxml.jackson.annotation.JsonIgnoreProperties;/*** Created by SYJ on 2017/4/23.*/@JsonIgnoreProperties(ignoreUnknown= true)public classPerson {privateString name;privateString address;publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}publicString getAddress() {returnaddress;

}public voidsetAddress(String address) {this.address =address;

}

@OverridepublicString toString() {return "Person{" +

"name=‘" + name + ‘\‘‘ +

", address=‘" + address + ‘\‘‘ +

‘}‘;

}

}

这次再发送请求,就不会报400的错误了,后台接收到了数据,只是Person中没有的mail字段被忽略掉了:

20180110231200929810.png

@JsonIgnore:这个注解用来忽略某些字段,可以用在POJO类的字段或者Getter方法上,用在Setter方法时,和字段上效果一样。这个注解只能用在POJO存在的字段要忽略的情况。

@JsonIgnoreProperties(ignoreUnknown = true),将这个注解写在POJO类上之后,就会忽略类中不存在的字段。这个注解还可以指定要忽略的字段。使用方法如下:

@JsonIgnoreProperties({ "internalId", "secretKey" }),指定的字段不会被序列化和反序列化。

相反,如果json中少传一个字段呢?比如下面的请求中,只传了一个name属性,没有传address属性:

20180110231200963991.png

请求也成功了,只是address这个字段的值为null:

69c19b5d7a1b19c673a664daa279cac2.png

我们给Person类增加一个Integer类型的age属性:

importcom.fasterxml.jackson.annotation.JsonIgnoreProperties;/*** Created by SYJ on 2017/4/23.*/@JsonIgnoreProperties(ignoreUnknown= true)public classPerson {privateString name;privateInteger age;privateString address;publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}publicInteger getAge() {returnage;

}public voidsetAge(Integer age) {this.age =age;

}publicString getAddress() {returnaddress;

}public voidsetAddress(String address) {this.address =address;

}

@OverridepublicString toString() {return "Person{" +

"name=‘" + name + ‘\‘‘ +

", age=" + age +

", address=‘" + address + ‘\‘‘ +

‘}‘;

}

}

请求的json中,age的值可以加引号,也可以不加引号,后台会自动识别为Integer类型:

20180110231201021611.png

20180110231201037236.png

但是如果你给age设置了一个不能转成整数的值,会报如下错误:

20180110231201073371.png

错误信息的意思是说,qq不是一个有效的整数,也无法转成整数。

下面看一下返回值,上面都是返回一个String类型,下面我们直接返回一个对象Person:

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;import javax.ws.rs.*;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@POST

@Path("/test08")publicPersontest08(Person person) {

LOGGER.info(person.toString());returnperson;

}

}

发送请求:

20180110231201088020.png

响应500了:

20180110231201111458.png

后台服务器报错信息:

四月 23, 2017 4:44:54 下午 org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo

严重: MessageBodyWriter not found for media type=application/xml, type=class com.zhaopin.api.test.Person,

genericType=class com.zhaopin.api.test.Person.

因为默然返回的数据格式不是json,所以需要在方法上添加@Produces("application/json")注解来指定返回的数据格式:

20180110231201142709.png

再次请求,返回的Person对象被自动转成了json格式:

20180110231201172007.png

返回值如果是一个Map的话:

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;import javax.ws.rs.*;importjava.util.HashMap;importjava.util.Map;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@POST

@Path("/test08")

@Produces("application/json")publicMaptest08(Person person) {

LOGGER.info(person.toString());

HashMap map = new HashMap<>();

map.put("A", "a");

map.put("B", "b");returnmap;

}

}

请求:

20180110231201220837.png

响应:

20180110231201233533.png

说明,Map也是可以被自动转成json格式的。

如果返回值类型是一个List呢?

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;import javax.ws.rs.*;importjava.util.ArrayList;importjava.util.List;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@POST

@Path("/test09")

@Produces("application/json")public Listtest09(Person person) {

LOGGER.info(person.toString());

List list = new ArrayList<>();

list.add("A");

list.add("B");

list.add("C");returnlist;

}

}

请求:

20180110231201250135.png

响应结果:

20180110231201290176.png

返回的List被转成了一个json数组形式,说明List也能被成功转成json格式。

如果方法接收一个List,返回一个List呢?

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;import javax.ws.rs.*;importjava.util.List;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@POST

@Path("/test10")

@Produces("application/json")public List test10(ListpersonList) {

LOGGER.info(personList.toString());returnpersonList;

}

}

请求(Json数组中有一个Person的例子):

20180110231201308731.png

响应:

20180110231201331193.png

再请求(Json数组中有多个Person的例子):

20180110231201344865.png

响应:

20180110231201361468.png

下面我们将Person类搞复杂一点,给Person类增加一个List属性,表示一个人(Person)可以有多个朋友(朋友也是Person):

importcom.fasterxml.jackson.annotation.JsonIgnoreProperties;importjava.util.List;/*** Created by SYJ on 2017/4/23.*/@JsonIgnoreProperties(ignoreUnknown= true)public classPerson {privateString name;privateInteger age;privateString address;private Listfriends;publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}publicInteger getAge() {returnage;

}public voidsetAge(Integer age) {this.age =age;

}publicString getAddress() {returnaddress;

}public voidsetAddress(String address) {this.address =address;

}public ListgetFriends() {returnfriends;

}public void setFriends(Listfriends) {this.friends =friends;

}

@OverridepublicString toString() {return "Person{" +

"name=‘" + name + ‘\‘‘ +

", age=" + age +

", address=‘" + address + ‘\‘‘ +

", friends=" + friends +

‘}‘;

}

}

方法的参数和返回值都是Person:

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;import javax.ws.rs.*;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@POST

@Path("/test11")

@Produces("application/json")publicPersontest11(Personperson) {

LOGGER.info(person.toString());returnperson;

}

}

请求的json数据如下:

20180110231201373187.png

发送请求:

20180110231201401508.png

响应:

20180110231201422017.png

将返回的json数据格式化后如下:

20180110231201440572.png

你可能注意到了,上面的json数据中有两个null值,我有两个朋友分别是小张和小王,但是小张和小王没有朋友。

使用@PathParam注解来匹配获取URL路径中的参数:

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;import javax.ws.rs.*;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@GET

@Path("/test12/{name}")public String test12(@PathParam("name")String name) {

LOGGER.info("name=" +name);returnname;

}

}

发送请求,在url路径中传递参数,如下图所示发送了一个字符串"刘备",这种方式只支持GET请求:

20180110231201460104.png

响应:

20180110231201479636.png

说明通过@PathParam注解能够获取到URL路径中传递过来的参数。

下面通过注解来获取URL路径中传递的一个名为"name"的请求参数,这种方式同样只支持GET请求:

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;import javax.ws.rs.*;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@GET

@Path("/test13")public String test13(@QueryParam("name")String name) {

LOGGER.info("name=" +name);returnname;

}

}

发送请求,注意在URL路径中传递参数使用的格式是"?key=value"的形式:

20180110231201494285.png

响应:

20180110231201512841.png

下面是从URL中获取传递的多个参数(String类型的name和Integer类型的age):

importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;import javax.ws.rs.*;/*** Created by SYJ on 2017/4/23.*/@Component

@Path("/class")public classTestController {private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

@GET

@Path("/test13")public String test13(@QueryParam("name")String name, @QueryParam("age")Integer age) {

LOGGER.info("name=" + name + ",age=" +age);return "name=" + name + ",age=" +age;

}

}

发送GET请求,在URL中传递name和age两个参数:

20180110231201536279.png

得到的响应为:

20180110231201546045.png

如果没有传递name或者age:

20180110231201561671.png

请求也没有报错,得到的响应结果中,name和age的值都是null:

20180110231201575343.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值