当前位置: 移动技术网 > IT编程>开发语言>Java > RESTful详解

RESTful详解

2019年05月29日  | 移动技术网IT编程  | 我要评论

文章大纲

一、什么是restful
二、为什么要使用restful
三、restful实战
四、项目源码下载
五、参考文章

 

一、什么是restful

1. restful概念

  rest 是面向资源的,这个概念非常重要,而资源是通过 uri 进行暴露。
  uri 的设计只要负责把资源通过合理方式暴露出来就可以了。对资源的操作与它无关,操作是通过 http动词来体现,所以rest 通过 uri 暴露资源时,会强调不要在 uri 中出现动词。

比如:左边是错误的设计,而右边是正确的

get /rest/api/getdogs --> get /rest/api/dogs 获取所有小狗狗 
get /rest/api/adddogs --> post /rest/api/dogs 添加一个小狗狗 
get /rest/api/editdogs/:dog_id --> put /rest/api/dogs/:dog_id 修改一个小狗狗 
get /rest/api/deletedogs/:dog_id --> delete /rest/api/dogs/:dog_id 删除一个小狗狗

  rest很好地利用了http本身就有的一些特征,如http动词、http状态码、http报头等等
rest api 是基于 http的,所以你的api应该去使用 http的一些标准。这样所有的http客户端(如浏览器)才能够直接理解你的api(当然还有其他好处,如利于缓存等等)。rest 实际上也非常强调应该利用好 http本来就有的特征,而不是只把 http当成一个传输层这么简单了。

http/1.1 200 ok
content-type: application/json
content-length: xxx

{
   "url" : "/api/categories/1",
   "label" : "food",
   "items_url" : "/api/items?category=1",
   "brands" : [
         {
            "label" : "友臣",
            "brand_key" : "32073",
            "url" : "/api/brands/32073"
         }, {
            "label" : "乐事",
            "brand_key" : "56632",
            "url" : "/api/brands/56632"
         }
         ...
   ]
}

看这个响应,包含了http里面的状态码等信息。还会有http的一些报头。

authorization 认证报头 
cache-control 缓存报头 
cnotent-type  消息体类型报头 

2. rest 系统的特征

(1)客户-服务器(client-server),提供服务的服务器和使用服务的客户需要被隔离对待。
(2)无 状态(stateless),来自客户的每一个请求必须包含服务器处理该请求所需的所有信息。换句话说,服务器端不能存储来自某个客户的某个请求中的信息,并在该客户的其他请求中使用。
(3)可缓存(cachable),服务器必须让客户知道请求是否可以被缓存。(ross:更详细解释请参考 理解本真的rest架构风格 以及 stackoverflow 的这个问题 中对缓存的解释。)
(4)分层系统(layered system),服务器和客户之间的通信必须被这样标准化:允许服务器和客户之间的中间层(ross:代理,网关等)可以代替服务器对客户的请求进行回应,而且这些对客户来说不需要特别支持。
(5)统一接口(uniform interface),客户和服务器之间通信的方法必须是统一化的。(ross:get,post,put.delete, etc)
(6)支持按需代码(code-on-demand,可选),服务器可以提供一些代码或者脚本(ross:javascrpt,flash,etc)并在客户的运行环境中执行。这条准则是这些准则中唯一不必必须满足的一条。(ross:比如客户可以在客户端下载脚本生成密码访问服务器。)
其中重点对(2)中内容进行说明:
get(select):从服务器取出资源(一项或多项)。
post(create):在服务器新建一个资源。
put(update):在服务器更新资源(客户端提供完整资源数据)。
patch(update):在服务器更新资源(客户端提供需要修改的资源数据)。
delete(delete):从服务器删除资源。

二、为什么要使用restful

http是目前在互联网上使用最多的协议,没有之一。
  可是http的创始人一直都觉得,在过去10几年来,所有的人都在错误的使用http.这句话怎么说呢?
  如果说你要删除一个数据,以往的做法通常是 delete/{id} 
  如果你要更新一个数据,可能是post数据放body,然后方法是 update/{id}, 或者是artichle/{id}?method=update 
   这种做法让roy fielding很暴燥,他觉得这个世界不该这样的,所有的人都在误解而且在严重错误的误解http的设计初衷,好比是发明了火药却只用它来做烟花爆竹。
   那么正确的使用方式是什么呢?如果你要看rest各种特性,你恐怕真的很难理解rest,但是如果你看错误的使用http的人倒底儿了哪些错,什么是rest就特别容易理解了。 
  
常见使用错误一:混乱

  一万个人心里有一万个url的命名规则,url是统一资源定位符,重点是资源。而很多人却把它当成了万金油,每一个独立的虚拟的网页都可以随意使用,各种操作都能够迭加。这是混乱的来源之一。
比如:

https://localhost:8080/myweb/getuserbyid?id=1
https://localhost:8080/myweb/user/getbyid?id=1
https://localhost:8080/myweb/x/y?id=1

常见使用错误二:贪婪

  有状态和无状态全部混在一起。特别是在购物车或者是登录的应用中,经常刷新就丢失带来的用户体验简直棒棒哒。每一个请求并不能单独的响应一些功能,很多的功能混杂在一起里。这是人性贪婪的本质,也是各种hack的起源,只要能够把问题解决掉,总会有人用他认为最方便的方式去解决问题,比如说汽车门把手坏掉了直接系根绳子当把手,emmmm这样确实很棒啊。
  
常见使用错误三:无序

  返回的结果往往是很随意,各种错误信息本来就是用http的状态码构成的,可是很多人还是喜欢把错误信息返回在返回值中。最常见的就是code和message,当然对于这一点,我个人是保留疑问的,我的观点是,http本身的错误和服务器的内部错误还是需要在不断层面分开的,不能混在一起。可是在大神眼里并非如此。

那么怎么解决这些问题呢?

  强迫症患者的福音就是先颁规则,第一个规则就是明确url是什么,该怎么用。就是所有的url本质来讲,都应该是一种资源。一个独立的url地址,就是对应一个独一无二的资源。怎么样?这种感觉是不是棒棒哒?一个冰淇淋,一个老师,一间房子,在url上对应的都是一个资源,不会有多余的url跟他对应,也不会表示有多个url地址 
  注意,这里点的是url地址,并不是单独的参数,他就是一个/room/{room_id}这样的东西,举个栗子,/room/3242 这就表示3242号房间。这是一个清爽的世界啊,你想想,之前的url是什么都要,我开房,可能是/open/room/3242 我要退房可能是/exit/3242/room,我要打理房间,可能是room/3242?method=clean.够了!这些乱七八糟的东西全够了,让世界回归清爽的本质,一间房,就是/room/3242 没有别的url地址了。
  在过去的混乱世界里,经常用的就是get和post。如果不是因为get不支持大数据传输,我想连post都不会有人使用。(想像一下roy fielding在愤怒的对着电脑屏幕喊,http的method一共有八个,你们为毛只逮着get一只羊的毛薅薅薅薅薅)。
  而对资源最常见的操作是什么?crud,对不对,就是创建,读,更新,删除。再看http的method?是不是非常完美?其实也怪fielding老爷子一开始命名不准确,如果刚开始就是把get方法叫做read,put方法叫做update,post叫做create这该多好。。。
  你用一个get,大家又发现没什么限制没什么所谓,又很难理解put和post的差别,法无禁止即可为啊,呃,老爷子不要瞪我,我瞎说的。总之,这四种方法够不够你浪?你有本身找出来更多的对资源的操作来啊,我还有4个method没用过呢。如果这4个真的不够了,有什么问题,大不了我再重新更改http协议啊。其实简单说,对于rest理解到这里就够了。后续的东西,都是在这一条基础上空想出来的,比强迫症更强迫症,当然,无状态我是百分百支持的。以上的各种表述可能不太准确,也纯属是我的意淫和各种小道资料,并未考据,但是凭良心讲,我是早就看不惯黑暗年代里的url命名风格了,所以当时最早接触到rest的时候,瞬间就找到了真爱,我靠,这不就是我一直想要的答案吗?但是我一直想的仅仅是命名规范,从来没有把自己的思考角度放在一个url就是一个资源,所有的操作都是对资源的更改而言的角度上啊。所以你能理解到的程度,更多的就是在于你要弄清楚你要解决的什么问题,如果你的问题只是理解rest,恐怕你很理解,如果你的问题是怎么解决url混乱的问题,你反而很快能弄懂了~

三、restful实战

1. 新建spring boot项目

 
 
 
 

创建后项目结构如下:

 

2. pom.xml文件添加依赖

<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/pom/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
         xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>

    <groupid>com.wxc</groupid>
    <artifactid>restful-test</artifactid>
    <version>1.0-snapshot</version>

    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>1.4.0.release</version>
        <relativepath /> <!-- lookup parent from repository -->
    </parent>


    <properties>

        <project.build.sourceencoding>utf-8</project.build.sourceencoding>
        <project.reporting.outputencoding>utf-8</project.reporting.outputencoding>

        <!--spring boot 项目默认的编译版本是 1.6,如果我们想使用其他的编译版本我们就需要在 pom.xml 文件中定义一个变量-->
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <!-- 加入web开发的支持 -->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>

        <dependency>
            <groupid>org.mybatis.spring.boot</groupid>
            <artifactid>mybatis-spring-boot-starter</artifactid>
            <version>1.1.1</version>
        </dependency>

        <!--添加模板引擎(freemarker)依赖包-->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-freemarker</artifactid>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- maven的编译插件 -->
            <plugin>
                <groupid>org.apache.maven.plugins</groupid>
                <artifactid>maven-compiler-plugin</artifactid>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
                <configuration>
                    <!-- 没有该配置,devtools 不生效 -->
                    <fork>true</fork>
                    <addresources>true</addresources>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3. 编写测试类

com.wxc.test.controller包下创建usercontroller.java

package com.wxc.test.controller;

import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import org.springframework.web.bind.annotation.requestparam;
import org.springframework.web.bind.annotation.restcontroller;

@restcontroller
public class usercontroller
{

    @requestmapping(value = "/user", method = requestmethod.post)
    public string adduser( string user) {
        system.out.println("开始新增...");
        return "开始新增..."+"传过来参数为:"+user;
    }

    @requestmapping(value = "/user", method = requestmethod.put)
    public boolean updateuser( string  user) {
        system.out.println("开始更新...");
        return true;
    }

    @requestmapping(value = "/user", method = requestmethod.delete)
    public boolean delete(@requestparam(value = "username", required = true) int userid) {
        system.out.println("开始删除...");
        return true;
    }


    @requestmapping(value = "/user", method = requestmethod.get)
    public string findbyusername(@requestparam(value = "username", required = true) string username) {
        system.out.println("开始查询...");
        return "开始查询..."+"传过来的参数为:"+username;
    }


    @requestmapping(value = "/userall", method = requestmethod.get)
    public string findbyuserage() {
        system.out.println("开始查询所有数据...");
        return "开始查询所有数据...";
    }
}

4. 创建项目启动类

com.wxc.test包下创建application.java

package com.wxc.test;

import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;

@springbootapplication
public class application {

    public static void main(string[] args) {
        //入口运行类
        springapplication.run(application.class, args);

    }

}

创建后项目结构如下:

 

5. 运行项目并访问

 
 
 
 

可以看到,接口访问方式必须和指定的一致,比如post无法通过get进行请求

四、项目源码下载

链接:https://pan.baidu.com/s/1qyliadtgxuz7g0qytwzlaa
提取码:rg60

五、参考文章

    1. https://blog.csdn.net/qq_21383435/article/details/80032375

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网