欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

RESTful详解

程序员文章站 2022-03-20 08:37:13
文章大纲 一、什么是RESTful二、为什么要使用RESTful三、RESTful实战四、项目源码下载五、参考文章 一、什么是RESTful 1. RESTful概念 REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露。 URI 的设计只要负责把资源通过合理方式暴露出来就可以了 ......

文章大纲

一、什么是restful
二、为什么要使用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架构风格 以及 * 的这个问题 中对缓存的解释。)
(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项目

 
RESTful详解
 
RESTful详解
 
RESTful详解
 
RESTful详解

创建后项目结构如下:

 
RESTful详解

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);

    }

}

创建后项目结构如下:

 
RESTful详解

5. 运行项目并访问

 
RESTful详解
 
RESTful详解
 
RESTful详解
 
RESTful详解

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

四、项目源码下载

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

五、参考文章

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