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:

<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.0</version>
</dependency>

gradle:

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

二、编写测试代码

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @GET
    @Path("/")
    public String testMethod() {
        LOGGER.info("===TestController#test===");
        return "TestController#test";
    }
}

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

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

四:@Path注解的几个测试

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

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

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

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

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

@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @GET
    @Path("/")
    public String testMethod() {
        LOGGER.info("===TestController#test===");
        return "TestController#test";
    }

    @GET
    @Path("/test01")
    public String test01() {
        LOGGER.info("===TestController#test01===");
        return "TestController#test01";
    }
}

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

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

@POST注解测试:

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

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

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

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

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

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

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

/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @POST
    @Path("/test03")
    public String test03(@Context HttpServletRequest request) {
        if (request != null) {
            Enumeration<String> 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:

[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")方法来获取请求头中的信息:

测试获取请求体(RequestBody):

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

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

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

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

打印结果:

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

依然能够拿到数据:

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

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

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

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

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

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

/**
 * Created by SYJ on 2017/4/23.
 */
public class Person {
    private String name;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name=‘" + name + ‘\‘‘ +
                ", address=‘" + address + ‘\‘‘ +
                ‘}‘;
    }
}

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

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

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

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

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

四月 23, 2017 3:31:40 下午 org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor aroundReadFrom
严重: MessageBodyReader not 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对象中没有的字段会怎么样呢?

响应的状态码为400:

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

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

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

/**
 * Created by SYJ on 2017/4/23.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
    private String name;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name=‘" + name + ‘\‘‘ +
                ", address=‘" + address + ‘\‘‘ +
                ‘}‘;
    }
}

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

@JsonIgnore:这个注解用来忽略某些字段,可以用在POJO类的字段或者Getter方法上,用在Setter方法时,和字段上效果一样。这个注解只能用在POJO存在的字段要忽略的情况。
@JsonIgnoreProperties(ignoreUnknown = true),将这个注解写在POJO类上之后,就会忽略类中不存在的字段。这个注解还可以指定要忽略的字段。使用方法如下:
@JsonIgnoreProperties({ "internalId", "secretKey" }),指定的字段不会被序列化和反序列化。

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

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

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

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

/**
 * Created by SYJ on 2017/4/23.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
    private String name;
    private Integer age;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name=‘" + name + ‘\‘‘ +
                ", age=" + age +
                ", address=‘" + address + ‘\‘‘ +
                ‘}‘;
    }
}

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

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

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

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.ws.rs.*;

/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @POST
    @Path("/test08")
    public Person test08(Person person) {
        LOGGER.info(person.toString());
        return person;
    }
}

发送请求:

响应500了:

后台服务器报错信息:

四月 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")注解来指定返回的数据格式:

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

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import java.util.HashMap;
import java.util.Map;
/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @POST
    @Path("/test08")
    @Produces("application/json")
    public Map test08(Person person) {
        LOGGER.info(person.toString());
        HashMap<Object, Object> map = new HashMap<>();
        map.put("A", "a");
        map.put("B", "b");
        return map;
    }
}

请求:

响应:

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

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import java.util.ArrayList;
import java.util.List;
/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @POST
    @Path("/test09")
    @Produces("application/json")
    public List<Object> test09(Person person) {
        LOGGER.info(person.toString());
        List<Object> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        return list;
    }
}

请求:

响应结果:

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

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import java.util.List;
/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @POST
    @Path("/test10")
    @Produces("application/json")
    public List<Person> test10(List<Person> personList) {
        LOGGER.info(personList.toString());
        return personList;
    }
}

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

响应:

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

响应:

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

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.List;
/**
 * Created by SYJ on 2017/4/23.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
    private String name;
    private Integer age;
    private String address;
    private List<Person> friends;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Person> getFriends() {
        return friends;
    }

    public void setFriends(List<Person> friends) {
        this.friends = friends;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name=‘" + name + ‘\‘‘ +
                ", age=" + age +
                ", address=‘" + address + ‘\‘‘ +
                ", friends=" + friends +
                ‘}‘;
    }
}

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @POST
    @Path("/test11")
    @Produces("application/json")
    public Person test11(Person person) {
        LOGGER.info(person.toString());
        return person;
    }
}

请求的json数据如下:

发送请求:

响应:

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

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

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @GET
    @Path("/test12/{name}")public String test12(@PathParam("name")String name) {
        LOGGER.info("name=" + name);
        return name;
    }
}

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

响应:

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

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
    @GET
    @Path("/test13")
    public String test13(@QueryParam("name")String name) {
        LOGGER.info("name=" + name);
        return name;
    }
}

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

响应:

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
/**
 * Created by SYJ on 2017/4/23.
 */
@Component
@Path("/class")
public class TestController {
    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两个参数:

得到的响应为:

如果没有传递name或者age:

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

时间: 2024-11-03 21:39:03

Java基于JAX-RS开发Restful接口总结的相关文章

Java基于ssm框架的restful应用开发

Java基于ssm框架的restful应用开发 好几年都没写过java的应用了,这里记录下使用java ssm框架.jwt如何进行rest应用开发,文中会涉及到全局异常拦截处理.jwt校验.token拦截器等内容. 1.jwt工具类 直接贴代码了,主要包括jwt的sign.verify.decode三个方法,具体实现如下: package com.isoft.util; import java.util.Date; import com.auth0.jwt.JWT; import com.aut

Java文件上传:Restful接口接收上传文件,缓存在本地

接口代码 import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframewor

PHP restful 接口

首先我们来认识下RESTful Restful是一种设计风格而不是标准,比如一个接口原本是这样的: http://www.test.com/user/view/id/1 表示获取id为1的用户信息,如果使用Restful风格,可以变成这样: http://www.test.com/user/1 可以很明显的看出这样做的好处: 1.更简洁的URL,对程序员友好 2.不暴露内部代码结构,更安全 那么,如何实现这个接口呢?首先,我们需要接收到/user/1部分. $path = $_SERVER['P

基于反射实现自动化restful开发

[Author]: kwu 基于反射实现自动化restful开发,通用的只需要写查询数据库的sql,并加入对应的javabean实现的快速restful服务的开发. 1.编写数据库的查询sql,对应sql.properties daily = DailyReport;select t.day,t.cnt,t.type from (select day,cnt,type From dailyreport where type=? order by day desc limit ? ) t orde

基于cxf开发restful风格的Web Service

一.写在前面 webservice一些简单的其他用法和概念,就不在这里赘述了,相信大家都可以在网上查到,我也是一个新手,写这篇文章的目的一方面是想记录自己成长的历程,另一方面是因为学习这个的时候花了点时间,希望本文章能为大家节约点时间.当然描述的可能不到位,望谅解. 二.创建项目 2.1.创建公用接口project 为了方便服务端和客户端调用接口可以先创建一个接口java project,单独建一个接口project的用处后面再说. 然后导入对应jar包,可以去cxf官网下载http://cxf

基于Axis1.4的webservice接口开发(代码开发)

一.开发环境: 我的开发环境是MyEclipse 2015+Apache-Tomcat-8.0.21. 二.代码开发: 1.新建一个Web Project工程,并导入jar包(Axis1.4的环境搭建在上一篇博客http://www.cnblogs.com/zhukunqiang/p/7124977.html中有介绍): 1.在com.ll.server包下新建java类,工程结构目录如下: 2.java代码如下(由于该项目用于测试,简单点无所谓): package com.ll.server;

前端调用后端的方法(基于restful接口的mvc架构)

1.前端调用后台: 建议用你熟悉的一门服务端程序,例如ASP,PHP,JSP,C#这些都可以,然后把需要的数据从数据库中获得,回传给客户端浏览器(其实一般就是写到HTML中,或者生成XML文件)然后在用JS获得. 2.js只是前端的语言,它还没有访问数据库的能力.不过它可以向某个URL发送请求,并获得返回的数据.这个会用到Ajax技术. 用AJAX,页面不刷新,只提交字符串到后台导入数据库       通过纯AngularJS+REST API构建Web是否可行? 在构建Web系统的时候,可不可

java web开发(二) 接口开发

java web开发(一) 环境搭建讲解了如何搭建基础项目,如果你还没了解,可以先去看看!今天我们就来看看接口的开发,打算使用比较古老的或者说比较原始方法实现的接口. 一.数据库设计. 我打算做一个简单的学生信息管理系统,数据库名为students_manage,并且先设计一张学生表,表名为student. 1.打开Sqlyong工具,如果还没创建连接,可以点击新建,输入名称,点击确定即可, 2.然后在保存的连接中选择,刚刚新建的连接,只需要在密码输入框中输入,安装数据库时的设置的密码,点击连接

OGEngine —— 基于JAVA的手游开发开源引擎

OGEngine是国际著名开源引擎AndEngine的一个分支,遵循LGPL开源协议使用OpenGL ES进行图形绘制.同时集成了Box2D物理引擎,因此可以实现复杂的物理效果. OGEngine主要使用Java语言开发,但在大运算量的耗时功能时,OGEngine使用了C/C++本地代码进行开发.比如物理引擎及音频处理.作为用户,你只需要关注Java端就可以了,它已经把所有的本地代码封装好了.相比于其他android游戏引擎,OGEngine的效率优势十分明显. AndEngine在国际上已成为