HI, Jersey - Restful webservice

一. 概念 rest

全称 (Resource) REpresentational State Transfer (表现层状态转移)
rest 描述的是 client 到 server的一种交互形式
Resource 资源 代表了数据
REpresentational 资源的表现形式 如json xml text...
State 状态
State Transfer 状态转变化 主要通过 http 的一系列操作实现

二. 概念 restful

restful 是API的一种设计风格,这个风格里面规定资源的操作CRUD(create, reach, update, delete)通过Http方法 post get put
delete 来实现:

rest http des
create post 添加
reach get 获取
update put 更新
delete delete 删除

在restful 里面 万物皆资源,那要操作这些资源,怎么对这些资源进行定位呢? 答: 通过url.

通常情况下 我们写url 如下

method api des
get api/getUser 获取用户
post api/addUser 添加用户
post api/updateUser 更新用户信息
post api/deleteUser 删除用户

这是不符合restful 风格的, rest 不建议在url 中使用动词如表格中的get/add...来表述你的意图

那符合rest的url是什么样子的呢

method api des
get api/user 获取用户
post api/user 添加用户
put api/user 更新用户信息
delete api/user 删除用户

通俗点来讲就是 通过url就可以知道你想要的资源 通过http method就可以知道你想要的操作 通过http status 就可以知到操作的结果,并约束get 的操作不能改变资源的状态

推荐使用名词复数来定义所有资源
api/users  代替 api/user
使用子资源来表达资源间的关系

example: id 为12的用户下的所有订单

api/users/12/orders  

HATEOAS 约束

HATEOAS(Hypermedia as the engine of application state) 一种比较复杂的约束,我们通常的做法是和服务器端约定好接口的定义,然后接口连接hardcode在本地 需要的时候调用,这种方式使客户端和服务器端的耦合加重,如果服务器端修改了接口定义,那么客户端也要跟着修改,在 HATEOAS 资源的uri都是动态的所以当接口定义发生改变时 客户端并不需要做任何的修改。
example: 请求用户列表

{
  "users": [
   {
    "id": "23",
    "name": "Stefan Jauker",
    "links": [
     {
     "rel": "self",
     "href": "/api/v1/users/23"
    }
   ]
  }
 ]
}
Tips:
可以参考git接口定义
https://developer.github.com/v3/git/commits/

HATEOAS说明 
https://en.wikipedia.org/wiki/HATEOAS 

Http 状态码说明
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes 

从上到下 我们已经涵盖了Rest 的四种成熟度, 下面我们来看一下什么是Rest 的成熟度模型
1.第一个层次(Level 0)的 Web 服务只是使用 HTTP 作为传输方式,实际上只是远程方法调用(RPC)的一种具体形式。SOAP 和 XML-RPC 都属于此类。

Tips: RPC SOAP REST 对比
https://www.cnblogs.com/bellkosmos/p/5213491.html

2.第二个层次(Level 1)的 Web 服务引入了资源的概念。每个资源有对应的标识符和表达。
3.第三个层次(Level 2)的 Web 服务使用不同的 HTTP 方法来进行不同的操作,并且使用 HTTP 状态码来表示不同的结果。如 HTTP GET 方法来获取资源,HTTP DELETE 方法来删除资源。
4.第四个层次(Level 3)的 Web 服务使用 HATEOAS。在资源的表达中包含了链接信息。客户端可以根据链接来发现可以执行的动作。

talk is cheap show me the code

---------------------------------我是漂亮的分割线-----------------------------

下面篇文章介绍怎样使用jersey 实现restful api
遵循rest 风格的框架辣么多 为啥用jersey 呢?

为啥要用jersey?
因为简单呀 哇哈哈哈。。。。
首先附上 restful api 设计指南
http://www.ruanyifeng.com/blog/2014/05/restful_api.html

首先介绍几个annotation:

@GET @POST @PUT @ DELETE Http method
@Path 标识路径url
@Consumes 要求输入的数据类型 MediaType (application/json ...)
@Produces 接口返回的数据类型 MediaType (同上)
@PathParam 路径参数: /user /{param}
@QueryParam get请求url 参数 /user?id=12
@FormParam 表单参数

以上是常用的几个注解, 下面开始讲实际操作
准备好原料 eclipse/ tomcat /jersey libs

一 新建web项目

不会的找度娘

二 将lib是里面的jar 包全部复制到 web-info lib & ext下, 对就是那么暴力 。。。
image.png
三 新建package

例如 com.demo.api

全局配置

@ApplicationPath("/rest")
public class RestResourceConfig extends ResourceConfig {
    
    public RestResourceConfig() {
        packages("com.rest.demo.resource");
        register(UserRest.class);
    }
}

或者通过 web.xml 中配置
如果是基于web 3.0 新建的项目的话 是没有web.xml 的, 那么你需要新建一个web.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>HiJersey</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  
    <servlet>
        <servlet-name>Way REST Service</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer
        </servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.demo.api</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Way REST Service</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

新建类


@Path("/manager")
public class ManagerResource {
}

这样就简单的暴露了 一个resource
ps: @path 不是必须的, /不是必须的

下面我们来实现一个简单的get service

@GET 
public String sayHi() {
    return "hi jersey";
}

run on server 打开浏览器访问

http://localhost:8080/HiJersey/rest/manager

可以看到结果

image.png

很简单有木有
下面我们来实现稍微复杂一点的api

1.get 请求 带路径参数和查询参数

@GET
@Path("/user/{id}")
@Produces(MediaType.APPLICATION_JSON)
public User getUser(@PathParam("id") String id, @QueryParam("name") String name) {
        User user = new User(id, 18, name);
        return user;
}

注意 这边返回的是一个自定义类 那需要在User 类上注释@XmlRootElement 如下

@XmlRootElement
public class User implements Serializable{
}

同样可以返回 List & Map 类型的数据

  1. post 请求 接收对象参数
@POST
@Path("user")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public User addUser(User user) {
    return user;
}
  1. post 接收表单数据
    @POST
    @Path("login")
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Produces(MediaType.APPLICATION_JSON)
    public User login(MultivaluedMap<String, String> formParams) {
        User user = new User();
        user.setName(formParams.getFirst("name").toString());
        user.setPassword(formParams.getFirst("password").toString());
        
        return user;
    }
    
    @Path("login2")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public User login(@FormParam("name") String name, @FormParam("password") String password) {
        User user = new User();
        user.setName(name);
        user.setPassword(password);
        return user;
    }

put delete 方式传参和返回数据同上

  1. 文件上传
    @POST
    @Path("file")
    @Consumes({MediaType.MULTIPART_FORM_DATA})
    @Produces({MediaType.APPLICATION_JSON})
    public List<String>  upload(@Context HttpServletRequest request, @Context HttpServletResponse response) {
         String path = "D:"+File.separator+"imgs" + File.separator;
          return FileUtil.upload(path, request);
    }

第二种方式 so easy

    @POST
    @Path("file2")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public String upload2(
            @FormDataParam("file") InputStream input,
            @FormDataParam("file") FormDataContentDisposition d) {
        FileUtil.saveFile(input, "D:"+File.separator+"imgs" + File.separator+d.getFileName());
        return d.getFileName();
    }

注意这种方式需要配置 xml 并且需要加入multipart 和 mimepull包

<init-param>
  <param-name>jersey.config.server.provider.classnames</param-name>
  <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>

*注意: form 要设置成enctype='multipart/form-data'

5.获取所有路径参数 和查询参数

    @GET
    @Path("{version}")
    public String get(@Context UriInfo ui) {
        MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
        MultivaluedMap<String, String> pathPatams = ui.getPathParameters();
        for(String key : queryParams.keySet()) {
            System.out.println(key +"  " + queryParams.getFirst(key));
        }
        
        for(String key : pathPatams.keySet()) {
            System.out.println(key +"  " + pathPatams.getFirst(key));
        }
        return "success";
    }

以上就是jersey 的最基础的用法 over

点我 下载demo


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,601评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,367评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,249评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,539评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,967评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,929评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,098评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,825评论 0 207
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,609评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,796评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,282评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,603评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,277评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,159评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,959评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,079评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,874评论 2 277

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,108评论 18 139
  • 一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式。”但是在要求详细讲述它所提出的各个约束,以及如...
    时待吾阅读 3,342评论 0 19
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,402评论 6 342
  • 沈沁和萧霖是相亲认识的,85后式的相亲,程序极为简单,媒人安排男女双方见面,父母以及七大姑八大姨的陪同,看对...
    廊下阅读 893评论 0 2
  • 周四和周三大盘盘中均呈现出冲高回落状态,周四相较于周三冲高的点和收盘有下移状态,收在所有均线之下,空头明显,成交量...
    融河直播阅读 175评论 0 0