zh826256645 / RESTful-Test

Study RESTful
3 stars 0 forks source link

Study RESTful Note #1

Open zh826256645 opened 8 years ago

zh826256645 commented 8 years ago

Index

by @zh826256645

zh826256645 commented 8 years ago

第 1 章 JAX-RS2 入门

1.1 REST、REST服务、JAX-RS、Jersey项目是什么?

RESTful Web Service

  • REST 是一种跨平台、跨语言的架构风格
  • REST 式的 Web 服务是对 REST 在 Web 领域的实现;
  • JAX-RS 标准是 Java 领域对 REST 式的 Web 服务制定的实现标准
  • Jersey 是 JAX-RS 标准心的参考实现

1.2 解读 REST

  • 由来
    • REST(Representational State Transfer) 翻译为表述性状态转移
    • 由 Roy Thomas Fielding 博士在 2000 年的论文中提出
  • 特点
    • 客户端-服务器的、无状态的、可缓存的、统一接口、分层系统和按需编码
  • 架构风格
    • REST 是一种架构风格,并非一种技术(technology),与之对应的是 RPC 架构风格
    • 在这种架构风格中,对象被视为一种资源(resource)
    • 表述性状态是指资源数据在某个瞬时的状态快照
    • 请求一个资源的构成可以理解为访问一个具有特定性和描述性的 URL
  • 基本实现形式
    • HTTP + URL + XML 是 REST 的基本实现形式,但不是唯一的实现形式

1.3 解读 REST 服务

RESTful Web Service 的准确翻译应该是 REST 式的 WEB 服务,我们通常简称为 Web 服务

  • REST 式的 WEB 服务
    • RESTful Web Service 是一种遵守 REST 式风格的 Web 服务
    • REST 服务是一种 ROA(Resource-Oriented Architecture,面向资源的框架) 应用
    • 特点是方法信息存在于 HTTP 协议的方法中(比如 GET、PUT),作用域存在于 URL 中
  • 对比 RPC 风格
    • REST 风格更轻量和快速
    • RPC 是面向方法调用过程的,REST 是面向资源状态的
    • RPC 风格的两个代表是 XML-RPC 和大 WEB 服务
    • 大 Web 服务和 REST 式的 Web 服务各有优势,并不是一种替换关系
    • 在实际开发中两者共存于一个项目中也是一种解决方案
  • 对比 MVC 风格
    • MVC 风格偏重于解决服务器端的逻辑分层问题,以及客户端是逻辑分层的延伸问题
    • REST 风格偏重于统一接口,因此具体实现就可以跨平台和跨语言
    • MVC 和 REST 式并不是互斥的

1.4 解读 JAX-RS 标准

JAX-RS 是 Java 领域的 REST 式的 Web 服务的标准规范

  • JAX-RS2 的目标
    • 基于 POJO:规定使用 POJO 来公布 Web 资源
    • 以 HTTP 为中心
    • 格式独立性:对传输数据(HTTP Entity)的类型/格式的支持非常宽泛
    • 容器独立性:JAX-RS2 的应用可以部署到各种 Servlet 容器中
    • 内置 JaveEE:JAX-RS2 是 JavaEE 规范的一部分
  • 解读 JAX-RS 元素
    • 资源类:使用 JAX-RS 注解来实现相关 Web 资源的 Java 类
    • 通常,但不是约定,我们使用 resurce 作为包名,三层的包定义形如:resource-service-dao
    • 根资源类:使用 @Path 注解,资源类分为根资源类和子资源类
    • 请求方法标识符:使用运行期注解 @HttpMethod,用来标识处理资源的 HTTP 请求方法
    • 资源方法:资源类中定义的方法使用了请求方法标识符,用来处理相关资源的请求
    • 子资源标识符:资源类中定义的方法,用来定位相关资源的子资源
    • 子资源方法:资源类中定义的方法,用来处理相资源的子资源的请求
    • Provider:一种 JAX-RX 扩展接口的实现类,扩展了 JAX-RS 运行期的能力
    • Filter:一种用于过滤请求和响应的 Provider
    • WebTarget:一种使用 URL 标识的 Invocation 容器对象
    • Link:一种携带元数据的 URL,包括媒体类型、关系和标题等

1.5 Java REST 服务

资源类是接收 REST 请求并完成响应的核心类 资源类是由 REST 服务的“提供者”来调度的 这一概念类似其他框架中定义的 Servlet 类,该类会将请求分派给指定的 Controller/Action 类来处理 REST 中的这个提供者,即 JAX-RS2 中定义的 Application 以及 Servlet

  • REST 工程类型
    • 类型一:服务中没有 Application 子类,不存在 Servlet 子类
    • 类型二:不存在 Application 的子类,但存在 Servlet 的子类
    • 类型三:存在 Application 的子类并且定义了 @ApplicationPath 注解
    • 类型四:存在 Application 的子类,不存在 Servlet 子类、不存在或者不允许使用注解@ApplicationPath

1.6 REST 应用描述

  • 以 XML 格式展示当前 REST 环境中所提供的 REST 服务接口
  • 这种 XML 格式的描述就是 WADL(Web Application Description Language,Web 应用描述语言)
zh826256645 commented 8 years ago

第 2 章 REST API 设计

设计和开发 REST 式的 Web 服务除了要掌握 JAX-RS2 标准 还需要对统一接口资源定位以及请求处理过程中 REST 风格的传输数据的格式响应信息有良好的认知

2.1 统一接口

测试代码

  • REST 服务和 RPC 服务的区别
    • REST 使用 HTTP 协议的通用方法作为统一接口的标准词汇
    • REST 服务所提供的方法信息都在 HTTP 方法里
    • RPC 服务所提供的方法信息在 SOAP/HTTP 信封里(其封装格式通常是 HTTP 或 SOAP)
    • 每一个 RPC 式的 Web 服务都会公布一套符合自己商业逻辑的方法词汇
  • 如何统一 REST 接口
    • 每一种 HTTP 请求方法都可以从安全性和幂等性两方面考虑
    • 要定义严谨的 REST 统一接口,就需要真正理解 HTTP 方法的安全性和幂等性
    • 安全性(Safety)是指外系统对接口的访问,不会使服务端资源的状态发生改变
    • 幂等性 (idemptence)是值外系统对同一 REST 接口的多次访问,得到的资源状态是相同的

2.1.1 GET 方法

REST 使用 HTTP 的 GET 方法获取服务器提供的资源 GET 方法是只读的

  • 幂等性和安全性
    • GET 方法是幂等
    • 因为读取同一个资源,总是得到相同的数据
    • GET 方法也是安全
    • 因为读取资源不会对其状态做改动
    • 虽然 GET 方法的特性是幂等和安全的,但并不是说所有的定义为 GET 请求的方法都是幂等和安全的
    • 设计不良的 API 有可能违背 GET 的特性,将一个不该是 GET 的方法定义为之
  • GET 方法定义
    • 资源方法命名使用名词而非动词
    • 使用 @GET 注解资源类中所对应的方法

2.1.2 PUT 方法

PUT 方法是一种写操作的 HTTP 请求

  • 幂等性和安全性
    • PUT 方法是幂等的
    • 即多次插入或者更新同一份数据,在服务器对资源状态所产生的改变是相同的
    • PUT 方法是不安全的
    • 有写动作的 HTTP 方法都不是安全的
  • PUT 方法定义
    • 创建操作通常应设计为 POST 方法的 API
    • 唯有一种场景应当使用 PUT 方法来设计 API ,即客户端在发起创建时,在同一份数据中总可以提供唯一的组件
    • 使用 @PUT 注解资源类中所对应的方法
  • 媒体类型
    • 带有写操作的非安全性 HTTP 方法,需要考虑请求实体媒体类型和响应媒体类型
    • 请求实体媒体类型使用 HTTP 头的 Content Type 定义
    • 响应实体媒体类型使用 HTTP 头的 Accept 定义
    • 使用 @Consumes(MediaType.APPLICATION_XML) 定义服务器端要消费的媒体类型
    • 即消费客户端请求实体的类型
    • 使用 @Produces(MeidiaType.TEXT_PLAIN) 定义服务端生产的媒体类型
    • 即服务器产生的响应实体的媒体类型
    • javax.ws.rs.core.MediaType 类是 JAX-RS2 提供的媒体类型定义类

2.1.3 DELETE 方法

  • 幂等性和安全性
    • DELETE 方法是幂等的
    • 即多次删除一份数据,在服务器端产生的改变是相同的
    • DELETE 方法是不安全的
    • 其返回值可以定义为 void
    • 业务逻辑更关注删除操作的结果状态
  • DELETE 方法定义
    • 使用 @DELETE 注解资源类中所对应的方法

2.1.4 POST 方法

RPC 的所有写操作均使用 POST 方法 REST 只使用 HTTP 的 POST 方法添加资源

  • 幂等性和安全性
    • 定义为 POST 的 REST 接口用于写数据,POST 方法的特性是即不幂等也不安全
  • POST 方法定义
    • 使用 @POST 注解资源类中所对应的方法

2.1.5 WebDAV 扩展方法

WebDAV(Web-based Distributed Authoring and Versioning,基于 Web 的分布式创作与版本控制) HTTP1,1 协议的一组扩展

  • 增加的方法
    • PROPFIND 方法:用于从 Web 资源中查询储存为 XML 格式的属性数据,或者重载为从一个远程系统中查询目录结构的数据
    • PROPPATCH 方法:用于原子地更改和删除一个资源的多个属性
    • MKCOL 方法:用于创建目录
    • COPY 方法:用于将资源从一个 URL 资源地址复制到另一个 URL 资源地址
    • MOVE 方法:用于将资源从一个 URL 资源地址移动到另一个 URL 资源地址
    • LOCK 方法:用于锁定一个资源
    • WebDAV 支持共享锁和独占锁
    • UNLOCK 方法:用于解锁一个资源

2.2 资源定位

REST 使用 URL 实现资源定位 对外提供 REST 式的 Web 服务接口就是公布一系列的 URL 及其参数

  • 资源地址设计的重要性
    • 资源地址的设计是非常严谨的
    • 如果设计不得体,不仅 REST 接口的风格无法统一,使系统的扩展性和易用性降低,也很难实现资源准确地被定位
    • 资源地址设计的设计过程是面向资源的
    • 资源名称应是准确描述该资源的名词
    • 一个 URL 资源地址唯一对应一个资源,但是一个资源可以拥有多个 URL 资源地址

2.2.1 资源地址设计

资源地址的设计对整个 REST 式的 Web 服务至关重要 涉及系统的可用性、可维护性和可扩展性 HTTP 方法和资源地址结合在一起才可以完成对资源的定位

  • 资源路径(scheme://host:port/path?queryString)
    • scheme:协议名称,通常是 HTTP 和 HTTPS
    • host:(DNS)主机名称或者 IP 地址
    • port:服务端口
    • path:资源地址,使用“/”符号来分隔逻辑上的层次结构
    • ?:用来分隔资源地址和查询字符串符号
    • queryString:查询字符串,方法作用域信息
  • 资源地址示例 image
  • 资源地址解析
    • ContextPath:上下名称,通常和部署服务器的配置或者 REST 服务的 web.xml 配置有关
    • ServletPath:Servlet 名称,与 REST 服务中定义的 @ApplicationPath 注解或者 web.xml 的配置有关
    • PathInfo:是资源路径信息,与资源类、子类以及类中的方法定义的 @Path 注解有关
  • 资源地址和作用域
    • 问号(?)使用用来分隔资源地址和查询字符串的,与符号(&)是用来分隔查询条件的
    • GET /book?start=0&size=10
    • 逗号(,)是用来分隔有次序的作用域信息
    • GET /books/01,2012-12,2014
    • 分号(;)是用来分隔无次序的作用域信息
    • GET /books/restful;program=java;type=web
    • 常用的资源地址设计实例
    • 添加/创建:POST /books ; PUT /books/{id}
    • 删除:DELETE /books/{id}
    • 修改/更新:PUT /books/{id}
    • 查询全部:GET /book HTTP1.1
    • 主键查询:GET /books/{id} HTTP1.1 ; GET /books?id=12345678
    • 分页查询:GET /books?start=0&size=10 ; GET /books/01,2012-12,2013 ; GET /books/restful;program=java;type=web ; GET /books?limit=100&sort=bookname

2.2.2 @QueryParam 注解

方法代码 查询条件决定了方法的作用域,查询参数组成了查询条件 JAX-RS2 定义了 @QueryParam 注解来定义查询参数

  • 示例
    /*http://localhost:9998/query-resource/sorted-yijings?limit=5&sort=pronounce*/
    @Path("yijings")
    @GET
    @Produces({MediaType.APPLICATION_JSON , MediaType.APPLICATION_XML})
    // @QueryParam 定义查询参数
    public Yijings getByPaging (@QueryParam("start")final int start , @QueryParam("size")final int size) {
  • 注解 QueryParam 可以和注解 DefaultValue 一起使用
    • 注解 DefaultValue 的作用是预设一个默认值,请求中不包含此参数时使用
      @DefaultValue("100") @QueryParam("size") final Integer pagesize

2.2.3 @PathParam 注解

JAX-RS2 定义了 @PathParam 注解来定义路径参数 每个参数对应一个子资源

  • @ PathParam 示例列表
接口描述 资源地址
基本参数路径 /path-resource/Eric
集合查询参数 /path-resourc/Eric?hometown=Buenos Aires
带有标点符号的资源路径 /path-resource/199-1999
/path-resource/01,2012-12,2014
子资源变长的资源路径 /path-resource/Asia/China/northeast/liaoning/shen yang/huangu
/path- resource/q/restful;program=java;type=web
/path-resource/q2/restful;program=java;type=web
  • @Path 注解
    • JAX-RS2 定义了 @Path 注解来定义资源路径
    • @Path 接收一个 value 参数来解析资源路径地址
    • 可以使用动态变量的方法,其格式为:{参数名称:正则表达式}
    @GET
    @Path("{user:[a-zA-Z][a-zA-Z_0-9]*}")
    @Produces(MediaType.TEXT_PLAIN)
    public String getUserInfo(@PathParam("user") final String user , @DefaultValue("China") @QueryParam("hometown")final String hometown) {
        return user + ":" + hometown;
    }
  • 正则表达式
    • \d+:代表参数应该为数字并且至少出现一次
    • 加号(+):是至少出现一次
    • 星号(*):代表出现至少零次
    • 句号(.):匹配任何字符
    • d:匹配数字
    • w:匹配数字和字幕
正则表达式 含义
[a-zA-Z][a-zA-Z_0-9]* 以字母开头,后面是零到多个“字母_数字”格式的字符组合
{region:.+}/{districe:\w+} region 变量至少包含一个任意字符
district 变量至少包含一个为数字或者字母的字符
  • 路径区间
    • 路径区间(PathSegment)是对资源地址更灵活的支持
    • 使资源类的一个方法可以支持更广泛的资源地址的请求
    /*http://localhost:9998/path-resource/ZhongHao/China/southern/GuangDong/meizhou/pingyuan*/
    /*http://localhost:9998/path-resource/China/southern/GuangDong/meizhou/pingyuan*/
    /*http://localhost:9998/path-resource/southern/GuangDong/meizhou/pingyuan*/
    @GET
    @Path("{region:.+}/meizhou/{district:\\w+}")
    @Produces(MediaType.TEXT_PLAIN)
    public String getByAddress(@PathParam("region") final List<PathSegment> region , @PathParam("district") final String district) {
        final StringBuilder result = new StringBuilder();
        for(final PathSegment pathSegment : region) {
            result.append(pathSegment.getPath()).append("-");
        }
        result.append("meizhou-").append(district);
        final String r = result.toString();
        PathResource.LOGGER.debug(r);
        return r;
    }
  • PathSegment
    • 如其名字所示,是路径的片段,是资源的集合
    • 遍历 PathSegment 集合,对每一个 PathSegment 实力,可以调用其 getPath() 方法获取资源名称

2.2.4 @FormParam注解

JAX-RS2 定义了 @FormParam 注解来定义表单参数 相应的 REST 方法用于处理实体媒体类型为 Content-Type:application/x-www-form-urlencoded 的请求

@Path("form-resource")
public class FormResource {

    public static final String USER = "user";
    public static final String PW = "password";
    public static final String NPW = "newPassword";
    public static final String VNPW = "verification";

    @POST
    public String newPassword(
            @DefaultValue("ZhongHao") @FormParam(FormResource.USER) final String user 
            , @Encoded @FormParam(FormResource.PW) final String password 
            , @Encoded @FormParam(FormResource.NPW)final String newPassword 
            , @FormParam(FormResource.VNPW) final String verification) {
        return user + ":" + password + ":" + newPassword + ":" + verification;

    }
}
  • JAX-RS2 定义了 @Encoded 注解了用以标识禁用自动编码
    • 当对 newPassword 使用了 @Encoded 注解,REST 方法得到的参数值就会被解码
    • 如果将其直接返回,那么客户端得到的值就会是出于编码状态的字符串

2.2.5 @BeanParam 注解

JAX-RS2 定义了 @BeanParam 注解用于自定义参数组合 使 REST 方法可以使用简洁的参数形式完成复杂的接口设计

// 参数组合
public class Jaxrs2GuideParam {

    @HeaderParam("accept")
    private String acceptParam;

    @PathParam("region")
    private String regionParam;

    @PathParam("district")
    private String districtParam;

    @QueryParam("station")
    private String stationParam;

    @QueryParam("vehicle")
    private String vehicleParam;
    ...
public class BeanParamResource {
    @GET
    @Path("{region:.+}/GuangDong/{district:\\w+}")
    @Produces(MediaType.TEXT_PLAIN)
    public String getByAddress(@BeanParam Jaxrs2GuideParam param) {
        return param.getRegionParam() + ":" + param.getDistrictParam() + 
                ":" + param.getStationParam() + ":" + param.getVehicleParam();
    }
}

2.2.6 @CookieParam注解

JAX-RS2 定义了 @CookieParam 注解用以匹配 Cookie 中的键值对信息

    @GET
    public String getHeaderParams(@CookieParam("longitude") final String longitude 
            ,@CookieParam("latitude") final String latitude 
            ,@CookieParam("population") final double population 
            ,@CookieParam("area") final int area) {
        return longitude + "," + latitude + " population=" + population + ",area=" + area;
    }

2.2.7 @Context注解

JAX-RS2 定义了 @Context 注解来解析上下文参数 JAX-RS2 中有多种元素可以通过 @Context 注解作为上下文

public String getByAddress(@Context final Application application 
        , @Context final Request request 
        , @Context final Providers provider 
        , @Context final UriInfo uriInfo 
        , @Context final HttpHeaders headers) {
类名 含义
Application 应用上下文
Request 请求上下文
Providers 扩展类上下文
UriInfo 路径信息上下文
HttpHeaders 请求头信息上下文

2.3 传输格式

REST 接口会以 XML 和 JSON 作为主要的传输格式

2.3 基本类型

  • Java 的基本类型又叫原生类型
    • 4种整型(byte、short、int、long)
    • 2种浮点类型(float、double)
    • Unicode 编码的字符(char)和布尔类型(boolean)
  • Jersey 支持全部的基本类型,还支持与之相关的引用类型

2.3.2 文件类型

Jersey 支持传输 File 类型的数据,以方便客户端直接传递 File 类实例给服务器端

  • 文件类型请求,默认使用的媒体类型是 Content-Type:text/html
// 文件类型
// 默认使用的媒体类型是 Content-type:text/html
@POST
@Path("f")
public File postFile(final File f) throws FileNotFoundException, IOException {
    try(BufferedReader br = new BufferedReader(new FileReader(f))) {
        String s;
        do {
            s = br.readLine();
            LOGGER.debug(s);
        } while (s != null);
        return f;
    }
}

2.3.3 InputStream 类型

Jersey 支持 Java 的两大读写模式,即字节流和字符流

// 字节流类型
@POST
@Path("bio")
public String postStream(final InputStream in) throws IOException {
    try(BufferedReader br = new BufferedReader(
            // 需要设置编码
            new InputStreamReader(in,"utf-8"))) {
        StringBuilder result = new StringBuilder();
        String s = br.readLine();
        while(s != null) {
            result.append(s).append("\n");
            LOGGER.debug(s);
            s = br.readLine();
        }
        return result.toString();
    }
}

2.3.4 Reader 类型