• 凤凰框架笔记之RestFUl优化案例


    医生预约系统

    医生预约系统
    作为一名病人,我想要从系统中得知指定日期内我熟悉的医生是否具有空闲时间,以便于我向该医生预约就诊。

    传统的RPC编写方式

    • 医院开放了一个/appointmentService的 Web API,传入日期、医生姓名作为参数,可以得到该时间段该名医生的空闲时间,该 API 的一次 HTTP 调用如下所示:

      POST /appointmentService?action=query HTTP/1.1
      
      {date: "2020-03-04", doctor: "mjones"}
      
      • 1
      • 2
      • 3
    • 然后服务器会传回一个包含了所需信息的回应:

      HTTP/1.1 200 OK
      [
      	{start:"14:00", end: "14:50", doctor: "mjones"},
      	{start:"16:00", end: "16:50", doctor: "mjones"}
      ]
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 得到了医生空闲的结果后,我觉得 14:00 的时间比较合适,于是进行预约确认,并提交了我的基本信息:

      POST /appointmentService?action=confirm HTTP/1.1
      
      {
      	appointment: {date: "2020-03-04", start:"14:00", doctor: "mjones"},
      	patient: {name: icyfenix, age: 30, ……}
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 如果预约成功,那我能够收到一个预约成功的响应:

      HTTP/1.1 200 OK
      
      {
      	code: 0,
      	message: "Successful confirmation of appointment"
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 如果发生了问题,譬如有人在我前面抢先预约了,那么我会在响应中收到某种错误信息:

      HTTP/1.1 200 OK
      
      {
      	code: 1
      	message: "doctor not available"
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      到此,整个预约服务宣告完成

    引入资源的概念

    第 0 级是 RPC 的风格,如果需求永远不会变化,也不会增加,那它完全可以良好地工作下去。

    • 但是,如果你不想为预约医生之外的其他操作、为获取空闲时间之外的其他信息去编写额外的方法,或者改动现有方法的接口,那还是应该考虑一下如何使用 REST 来抽象资源。

    我们可以吧医生的信息看做资源,每个资源存在一个id。通过拼接url来完成参数的命中:

    POST /doctors/mjones HTTP/1.1
    
    {date: "2020-03-04"}
    
    • 1
    • 2
    • 3

    然后服务器传回一组包含了 ID 信息的档期清单,注意,ID 是资源的唯一编号,有 ID 即代表“医生的档期”被视为一种资源:

    HTTP/1.1 200 OK
    
    [
    	{id: 1234, start:"14:00", end: "14:50", doctor: "mjones"},
    	{id: 5678, start:"16:00", end: "16:50", doctor: "mjones"}
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我还是觉得 14:00 的时间比较合适,于是又进行预约确认,并提交了我的基本信息:

    POST /schedules/1234 HTTP/1.1
    
    {name: icyfenix, age: 30, ……}
    
    • 1
    • 2
    • 3

    比起第 0 级,第 1 级的特征是引入了资源,通过资源 ID 作为主要线索与服务交互,但第 1 级至少还有三个问题并没有解决:

    • 一是只处理了查询和预约,如果我临时想换个时间,要调整预约,或者我的病忽然好了,想删除预约,这都需要提供新的服务接口。
    • 二是处理结果响应时,只能靠着结果中的code、message这些字段做分支判断,每一套服务都要设计可能发生错误的 code,这很难考虑全面,而且也不利于对某些通用的错误做统一处理;
    • 三是并没有考虑认证授权等安全方面的内容,譬如要求只有登陆用户才允许查询医生档期时间,某些医生可能只对 VIP 开放,需要特定级别的病人才能预约,等等。

    引入统一接口,映射 HTTP 协议

    正对于上面的三个问题提出解决方案:

    • REST 的做法是把不同业务需求抽象为对资源的增加、修改、删除等操作来解决第一个问题;
    • 使用 HTTP 协议的 Status Code,可以涵盖大多数资源操作可能出现的异常,而且 Status Code 可以自定义扩展,以此解决第二个问题;
    • 依靠 HTTP Header 中携带的额外认证、授权信息来解决第三个问题,这个在实战中并没有体现,请参考安全架构中的“凭证”相关内容。

    按这个思路,获取医生档期,应采用具有查询语义的 GET 操作进行:

    GET /doctors/mjones/schedule?date=2020-03-04&status=open HTTP/1.1
    
    • 1
    • 然后服务器会传回一个包含了所需信息的回应:
      HTTP/1.1 200 OK
      
      [
      	{id: 1234, start:"14:00", end: "14:50", doctor: "mjones"},
      	{id: 5678, start:"16:00", end: "16:50", doctor: "mjones"}
      ]
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    我仍然觉得 14:00 的时间比较合适,于是又进行预约确认,并提交了我的基本信息,用以创建预约,这是符合 POST 的语义的:

    POST /schedules/1234 HTTP/1.1
    
    {name: icyfenix, age: 30, ……}
    
    • 1
    • 2
    • 3
    • 如果预约成功,那我能够收到一个预约成功的响应:

      HTTP/1.1 201 Created
      
      Successful confirmation of appointment
      
      • 1
      • 2
      • 3
    • 如果发生了问题,譬如有人在我前面抢先预约了,那么我会在响应中收到某种错误信息:

      HTTP/1.1 409 Conflict
      
      doctor not available
      
      • 1
      • 2
      • 3

    超文本驱动,进一步解耦

    在第二问中我们直接定死了 /schedules/1234 即通过这个url就可以访问指定id下的资源,可以进一步解耦,将这个 url的后缀也存在起来,进而解耦方便修改。

    • 当你输入了查询的指令之后:
      GET /doctors/mjones/schedule?date=2020-03-04&status=open HTTP/1.1
      
      • 1
    • 服务器传回的响应信息应该包括诸如如何预约档期、如何了解医生信息等可能的后续操作:
      HTTP/1.1 200 OK
      
      {
      	schedules:[
      		{
      			id: 1234, start:"14:00", end: "14:50", doctor: "mjones",
      			links: [
      				{rel: "comfirm schedule", href: "/schedules/1234"}
      			]
      		},
      		{
      			id: 5678, start:"16:00", end: "16:50", doctor: "mjones",
      			links: [
      				{rel: "comfirm schedule", href: "/schedules/5678"}
      			]
      		}
      	],
      	links: [
      		{rel: "doctor info", href: "/doctors/mjones/info"}
      	]
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
  • 相关阅读:
    HTML+CSS网页设计期末课程大作业 【茶叶文化网站设计题材】web前端开发技术 web课程设计 网页规划与设计
    【PTHREAD】线程状态
    6 年大厂程序员跟你聊聊,算法其实没那么难,要怎么准备比较好
    【Samba】win 11 不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接
    STM32笔记2-使用库函数点亮LED灯
    Oracle 12C的闪回技术详解
    利用wasm实现读写本地项目的在线编辑器
    【学习笔记】CF1713F Lost Array
    java报告:小不点超市售货系统类图设计
    【Python】-- Turtle绘图(使用代码画喜欢的图形!)
  • 原文地址:https://blog.csdn.net/Mr_tianyanxiaobai/article/details/127430418