【体系-单体架构】14-模块间的通信

1.后台管理与前台页面(门户)

概述:CMS(内容管理系统)具有发表文章、新闻、博客等功能,其涉及SEO(搜索引擎优化)能够动态生成HTML(例如:/2018/07/02/1001/1/1.html)

图解:后台数据给管理人员使用(用户,即运营)、前台(门户)数据给会员(客户)使用。其目的是,若运用传统模式开发将其都编写到同一项目中那么后台前台需要同时部署,此时若需要重启则需要前后台同时重启(同时耽误前台客户和后台运营访问),并且因为ip地址公开则可能会探测到其后台地址,存在安全隐患。若运用下图模式部署,门户(部署在公网)与后台(部署在局域网)是隔离开的,这样可防止入侵后台,提高系统整体安全性
在这里插入图片描述


2.模块化部署优势分析

后台系统:主要是对数据库进行维护,对数据库中表进行CRUD操作
应用接口:请求数据库作查询接口,专作查询使用(Web服务,无展示层)
门户系统:通过中间层(应用接口)请求数据库获取数据,门户系统通过API获取数据进行展示
优势分析:前后台的分离,通过中间层产生联系,故而安全性高;开发维护更加便利,可将三系统分别交由三团队进行维护,对任务划分更加明确;业务上的深层解耦;趋于分布式系统架构,服务器压力可以更加分散;单个系统维护、停运不会影响到其他系统的使用
在这里插入图片描述
数据流程:API(应用程序接口,负责给程序提供相关服务,例如查询、登录等)供客户端调用(例如:PC、安卓、H5、IOS)该统一的应用程序接口(都会调用API),而API返回统一的结果集,所以会封装一个统一的结构体交由客户端进行处理(而并非UI层直接进行展示),客户端将处理完成的数据进行展示层展示(该项目所谓前端是运用后台服务端技术开发前端,而不是专门的前端开发前端,真正前后端分离思想就不应该运用服务端技术去开发前端,而是应该用纯前端技术去开发前端)
浏览器与系统通信:浏览器通过HTTP协议(通讯协议)请求得到数据
模块与模块通信:门户模块通过浏览器访问接口模块的浏览器,此时使用HttpClient进行处理(即门户系统模拟人工进行请求门户模块浏览器)


3.Apache HttpClient 引入

概述:HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit (HTML解析器,用作爬虫。但常用的Java爬虫是Jsoup,因其更轻量、方便、快捷)都使用了 HttpClient。
爬虫:Java常用作爬虫的是Jsoup,但是一般都用python语言做爬虫而不是用Java语言,因为Java开发爬虫工作量大周期长(例如:今天开发下周使用),而python能够做到开箱即用(例如:上午开发上午使用)。但是Java爬虫效率远高于python,其效率最起码高出 python 10倍以上,python效率一直不太行
优势:HttpClient 相比传统 JDK 自带的 URLConnection,增加了易用性和灵活性,它不仅是客户端发送 HTTP 请求变得容易,而且也方便了开发人员测试接口(基于 HTTP 协议的),即提高了开发的效率,也方便提高代码的健壮性。因此熟练掌握 HttpClient 是很重要的必修内容,掌握 HttpClient 后,相信对于 HTTP 协议的了解会更加深入

特性:(可将其当成模拟/虚拟的浏览器)

  • 基于标准、纯净的 Java 语言(只依赖JDK,高内聚低耦合)。实现了 HTTP 1.0 和 HTTP 1.1
  • 以可扩展的面向对象的结构(符合面向对象设计原则,即开闭原则)实现了 HTTP 全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)
  • 支持 HTTPS 协议(支持SSL安全连接)。
  • 通过 HTTP 代理建立透明的连接(支持代理连接Proxy)。
  • 利用 CONNECT 方法通过 HTTP 代理建立隧道的 HTTPS 连接(支持以HTTPS的代理连接)。
    Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos 认证方案。
  • 插件式的自定义认证方案。
  • 便携可靠的套接字工厂(支持Socket)使它更容易的使用第三方解决方案。
  • 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接(支持并发请求)。
  • 自动处理 Set-Cookie 中的 Cookie。
  • 插件式的自定义 Cookie 策略(可以管理Cookie)。
  • Request 的输出流可以避免流中内容直接缓冲到 Socket 服务器。
  • Response 的输入流可以有效的从 Socket 服务器直接读取相应内容(支持Request/Response)。
  • 在 HTTP 1.0 和 HTTP 1.1 中利用 KeepAlive 保持持久连接(支持长连接)。
  • 直接获取服务器发送的 response code(Http Status 例如:200、404等) 和 headers。
  • 设置连接超时的能力。
  • 实验性的支持 HTTP 1.1 response caching。
  • 源代码基于 Apache License 可免费获取(开源、免费)。

人工操作浏览器

  • 打开浏览器
  • 输入 URL
  • 回车,请求
  • 展示,响应
  • 关闭浏览器

使用流程:使用 HttpClient 发送请求、接收响应很简单,一般需要如下几步即可(类似人工操作浏览器)

  • 创建 HttpClient 对象。
  • 创建请求方法的实例,并指定请求 URL。如果需要发送 GET 请求,创建 HttpGet 对象;如果需要发送 POST 请求,创建 HttpPost 对象。
  • 如果需要发送请求参数,可调用 HttpGet、HttpPost 共同的 setParams(HttpParams params) 方法来添加请求参数;对于 HttpPost 对象而言,也可调用 setEntity(HttpEntity entity) 方法来设置请求参数。
  • 调用 HttpClient 对象的 execute(HttpUriRequest request) 发送请求,该方法返回一个 HttpResponse。
  • 调用 HttpResponse 的 getAllHeaders()、getHeaders(String name) 等方法可获取服务器的响应头;调用 HttpResponse 的 getEntity() 方法可获取 HttpEntity 对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
  • 释放连接。无论执行方法是否成功,都必须释放连接

登录详解:因为我们在浏览器中使用会话(Session),所以浏览器知道用户在登录。tomcat这种服务器中间件都会管理会话,而会话(Session)其实就是cookie,即Session是一种由服务器管理的Cookie,Cookie则是由浏览器进行管理,因此基于这一特性,只要请求的时候带有已登录的Cookie则代表用户已登录,而Cookie则是由浏览器进行存储不然服务器无法管理。Cookie在F12>Application>Cookies中可找到

浏览器请求数据:找到Cookie将其复制,然后附入代码在这里插入图片描述

提示:复杂的框架最好不在现有项目里面直接追加,而是通过创建简单demo通晓其用法后,再在项目中添加使用

地址Architecture-MVC-myshop 框架示例源码 tag:3.3.3-RELEASE


4.Jackson 引入

概述:在上一步中,控制台成功打印出json字符串,这是因为我们在java代码中将数据对象转化成为json字符串,现在拿到json字符串需要将其转化为数据对象,实现这一转换我们就需要用到jackson工具类进行处理(理论是是目前效率最高的json解析器)

定义:Jackson 是一个简单基于 Java 应用库,Jackson 可以轻松的将 Java 对象转换成 json 对象和 xml 文档,同样也可以将 json、xml 转换成 Java 对象。Jackson 所依赖的 jar 包较少,简单易用并且性能也要相对高些,并且 Jackson 社区相对比较活跃,更新速度也比较快。(之前项目中的@ResponseBody注解返回的就是json数据,原因是项目之前引入过依赖jackson,也就是说spring自动用jackson把java对象转化成json字符串)

特点

  • 容易使用 - jackson API 提供了一个高层次外观(外观模式),以简化常用的用例。
  • 无需创建映射 - API提供了默认的映射大部分对象序列化。
  • 性能高 - 快速,低内存占用,适合大型对象图表或系统。
  • 干净的 JSON - jackson 创建一个干净和紧凑的 JSON 结果,这是让人很容易阅读。
  • 不依赖 - 库不需要任何其他的库,除了 JDK。
  • 开源代码 - jackson 是开源的,可以免费使用。

注解:Jackson 类库包含了很多注解,可以让我们快速建立 Java 类与 JSON 之间的关系,但是常用的就只有

  • @JsonProperty:指定一个属性用于 JSON 映射,默认情况下映射的 JSON 属性与注解的属性名称相同,不过可以使用该注解的 value 值修改 JSON 属性名,该注解还有一个 index 属性指定生成 JSON 属性的顺序,如果有必要的话
  • @JsonIgnore:用于排除某个属性,这样该属性就不会被 Jackson 序列化和反序列化

地址Architecture-MVC-myshop 框架示例源码 tag:3.3.4-RELEASE


5.API 接口模块创建

地址Architecture-MVC-myshop 框架示例源码 tag:3.3.5-RELEASE
注意事项:由于之前用8080端口启动my-shop-web-admin,此处启动my-shop-web-api的时候应该放在不同端口下,此时应该进行如下配置在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
补充

  • POJO:简单的Java对象,原生对象
  • VO(View Object):视图对象(HTML/JSP)
  • DTO(Data Transfer Object):数据传输对象(处理过的实体类转化的字符串,然后将其传递给门户,主要目的是取出冗余,省流。实质就是简化所要传输的数据)
  • Entity:实体类,与数据库一一对应,作ORM(对象关系映射)用
  • domain:领域(例如:银行、保险、物流、医疗)模型,产生于每个领域对对象看法不相同(例如:银行用户User称作客户,而医疗用户User称作病人),DDD(领域驱动设计)就是需要领域专家设计设计正常的领域模型,在开发时以领域为基准进行开发,那么开发出的产品/模块才更符合这个领域的使用

6.restful API 架构风格

技巧:很多很好的编程思想会体现在面试题里,面试题是编程精髓的提炼。所有实现都是基于论文支撑,所以创业也可以参考没有实现的论文

创业:如何找到项目

  • 找国外项目 TOP10直接抄,成功率高
  • 找文论,最好是刚发表的,将其实现

简介:REST 是“REpresentational State Transfer”的缩写,可以翻译成“表现状态转换”,但是在绝大多数场合中我们只说 REST 或者 RESTful。Fielding 在论文中将 REST 定位为“分布式超媒体应用(Distributed Hypermedia System)”的架构风格,它在文中提到一个名为“HATEOAS(Hypermedia as the engine of application state)”的概念。REST 是一种很笼统的概念,它代表一种架构风格。(软件架构风格,让api设计更优雅)

版本号:在 RESTful API 中,API 接口应该尽量兼容之前的版本(符合面向对象设计原则,开闭原则,面向修改关闭,面向扩展开放)。但是,在实际业务开发场景中,可能随着业务需求的不断迭代,现有的 API 接口无法支持旧版本的适配,此时如果强制升级服务端的 API 接口将导致客户端旧有功能出现故障。实际上,Web 端是部署在服务器,因此它可以很容易为了适配服务端的新的 API 接口进行版本升级,然而像 Android 端、IOS 端、PC 端等其他客户端是运行在用户的机器上,因此当前产品很难做到适配新的服务端的 API 接口,从而出现功能故障,这种情况下,用户必须升级产品到最新的版本才能正常使用。为了解决这个版本不兼容问题,在设计 RESTful API 的一种实用的做法是使用版本号。一般情况下,我们会在 url 中保留版本号,并同时兼容多个版本(修改不会基于原有API进行修改,规定api封版发布后就不能修改,除非BUG)
在这里插入图片描述

【GET】  /v1/users/{user_id}  // 版本 v1 的查询用户列表的 API 接口
【GET】  /v2/users/{user_id}  // 版本 v2 的查询用户列表的 API 接口

现在,我们可以不改变版本 v1 的查询用户列表的 API 接口的情况下,新增版本 v2 的查询用户列表的 API 接口以满足新的业务需求,此时,客户端的产品的新功能将请求新的服务端的 API 接口地址。虽然服务端会同时兼容多个版本,但是同时维护太多版本对于服务端而言是个不小的负担,因为服务端要维护多套代码。这种情况下,常见的做法不是维护所有的兼容版本(版本与版本之前有个过渡过程需要维护),而是只维护最新的几个兼容版本,例如维护最新的三个兼容版本。在一段时间后,当绝大多数用户升级到较新的版本后,废弃一些使用量较少的服务端的老版本API 接口版本,并要求使用产品的非常旧的版本的用户强制升级。

资源路径:RESTful API 的设计以资源为核心,每一个 URI 代表一种资源。因此,URI 不能包含动词,只能是名词。注意的是,形容词也是可以使用的,但是尽量少用。一般来说,不论资源是单个还是多个,API 的名词要以复数进行命名。此外,命名名词的时候,要使用小写、数字及下划线来区分多个单词。这样的设计是为了与 json 对象及属性的命名方案保持一致。例如,一个查询系统标签的接口可以进行如下设计

【GET】  /v1/tags/{tag_id} 

同时,资源的路径应该从根到子依次如下

/{resources}/{resource_id}/{sub_resources}/{sub_resource_id}/{sub_resource_property}

我们来看一个“添加用户的角色”的设计,其中“用户”是主资源,“角色”是子资源

【POST】  /v1/users/{user_id}/roles/{role_id} // 添加用户的角色

有的时候,当一个资源变化难以使用标准的 RESTful API 来命名,可以考虑使用一些特殊的 actions 命名。

/{resources}/{resource_id}/actions/{action}

举个例子,“密码修改”这个接口的命名很难完全使用名词来构建路径,此时可以引入 action 命名

【PUT】  /v1/users/{user_id}/password/actions/modify // 密码修改

请求方式:可以通过 GET、 POST、 PUT、 PATCH、 DELETE 等方式对服务端的资源进行操作。其中

  • GET:用于查询资源
  • POST:用于创建资源(GET获取数据、POST提交数据,大部分公司都只用这两种,但是都会遵循其语义)
  • PUT:用于更新服务端的资源的全部信息
  • PATCH:用于更新服务端的资源的部分信息(PUT/PATCH对应SQL update语义)
  • DELETE:用于删除服务端的资源(除了POST、GET其他三个都需要IE10支持)

这里,使用“用户”的案例进行回顾通过 GET、 POST、 PUT、 PATCH、 DELETE 等方式对服务端的资源进行操作:

类型路径解析
【GET】/users查询用户信息列表
【GET】/users/1001查看某个用户信息
【POST】/users新建用户信息
【PUT】/users/1001更新用户信息(全部字段)
【PATCH】/users/1001更新用户信息(部分字段)
【DELETE】/users/1001删除用户信息

查询参数:RESTful API 接口应该提供参数,过滤返回结果。其中,offset 指定返回记录的开始位置。一般情况下,它会结合 limit 来做分页的查询,这里 limit 指定返回记录的数量

【GET】  /{version}/{resources}/{resource_id}?offset=0&limit=20

同时,orderby 可以用来排序,但仅支持单个字符的排序,如果存在多个字段排序,需要业务中扩展其他参数进行支持。

【GET】  /{version}/{resources}/{resource_id}?orderby={field} [asc|desc]

为了更好地选择是否支持查询总数,我们可以使用 count 字段,count 表示返回数据是否包含总条数,它的默认值为 false

【GET】  /{version}/{resources}/{resource_id}?count=[true|false]

上面介绍的 offset、 limit、 orderby 是一些公共参数。此外,业务场景中还存在许多个性化的参数。我们来看一个例子

【GET】  /v1/categorys/{category_id}/apps/{app_id}?enable=[1|0]&os_type={field}&device_ids={field,field,}

注意的是,不要过度设计,只返回用户需要的查询参数。此外,需要考虑是否对查询参数创建数据库索引以提高查询性能

状态码:使用适合的状态码很重要,而不应该全部都返回状态码 200,或者随便乱使用。这里,列举在实际开发过程中常用的一些状态码,以供参考

状态码含义
200请求成功
201创建成功
400错误的请求
401未验证
403被拒绝
404无法找到
409资源冲突
500服务器内部错误

异常响应:当 RESTful API 接口出现非 2xx 的 HTTP 错误码响应时,采用全局的异常结构响应信息。该结构实际过程中不一定会用,因为返回的状态码可能暴露软件开发时所用语言、数据库等,对于黑客来说可以利用这些信息,所以对外暴露的时候尽量不要暴露这些信息,应尽量隐藏这类信息

HTTP/1.1 400 Bad Request
Content-Type: application/json
{
    "code": "INVALID_ARGUMENT",
    "message": "{error message}",
    "cause": "{cause message}",
    "request_id": "01234567-89ab-cdef-0123-456789abcdef",
    "host_id": "{server identity}",
    "server_time": "2014-01-01T12:00:00Z"
}

请求参数:在设计服务端的 RESTful API 的时候,我们还需要对请求参数进行限制说明。例如一个支持批量查询的接口,我们要考虑最大支持查询的数量

【GET】     /v1/users/batch?user_ids=1001,1002      // 批量查询用户信息
参数说明
- user_ids: 用户ID串,最多允许 20 个。

此外,在设计新增或修改接口时,我们还需要在文档中明确告诉调用者哪些参数是必填项,哪些是选填项,以及它们的边界值的限制

POST/v1/users                             // 创建用户信息
请求内容
{
    "username": "lusifer",                 // 必填, 用户名称, max 10
    "realname": "鲁斯菲尔",               // 必填, 用户名称, max 10
    "password": "123456",              // 必填, 用户密码, max 32
    "email": "topsale@vip.qq.com",     // 选填, 电子邮箱, max 32
    "weixin": "Lusifer",            // 选填,微信账号, max 32
    "sex": 1                           // 必填, 用户性别[1-男 2-女 99-未知]
}

响应参数:针对不同操作,服务端向用户返回的结果应该符合以下规范

【GET】     /{version}/{resources}/{resource_id}      // 返回单个资源对象
【GET】     /{version}/{resources}                    // 返回资源对象的列表
【POST】    /{version}/{resources}                    // 返回新生成的资源对象
【PUT】     /{version}/{resources}/{resource_id}      // 返回完整的资源对象
【PATCH】   /{version}/{resources}/{resource_id}      // 返回完整的资源对象
【DELETE】  /{version}/{resources}/{resource_id}      // 状态码 200,返回完整的资源对象。
                                                      // 状态码 204,返回一个空文档

如果是单条数据,则返回一个对象的 JSON 字符串

HTTP/1.1 200 OK
{
    "id" : "01234567-89ab-cdef-0123-456789abcdef",
    "name" : "example",
    "created_time": 1496676420000,
    "updated_time": 1496676420000,
    ...
}

如果是列表数据,则返回一个封装的结构体

HTTP/1.1 200 OK
{
    "count":100,
    "items":[
        {
            "id" : "01234567-89ab-cdef-0123-456789abcdef",
            "name" : "example",
            "created_time": 1496676420000,
            "updated_time": 1496676420000,
            ...
        },
        ...
    ]
}

案例:最后,我们使用一个完整的案例将前面介绍的知识整合起来。这里,使用“获取用户列表”的案例

GET/v1/users?[&keyword=xxx][&enable=1][&offset=0][&limit=20] 获取用户列表
功能说明:获取用户列表
请求方式:GET
参数说明
- keyword: 模糊查找的关键字。[选填]
- enable: 启用状态[1-启用 2-禁用][选填]
- offset: 获取位置偏移,从 0 开始。[选填]
- limit: 每次获取返回的条数,缺省为 20 条,最大不超过 100[选填]
响应内容(返回的对象必须是一个封装过的对象,而不能直接是集合)
HTTP/1.1 200 OK
{
    "count":100,
    "items":[
        {
            "id" : "01234567-89ab-cdef-0123-456789abcdef",
            "name" : "example",
            "created_time": 1496676420000,
            "updated_time": 1496676420000,
            ...
        },
        ...
    ]
}
失败响应
HTTP/1.1 403 UC/AUTH_DENIED
Content-Type: application/json
{
    "code": "INVALID_ARGUMENT",
    "message": "{error message}",
    "cause": "{cause message}",
    "request_id": "01234567-89ab-cdef-0123-456789abcdef",
    "host_id": "{server identity}",
    "server_time": "2014-01-01T12:00:00Z"
}
错误代码
- 403 UC/AUTH_DENIED    授权受限

案例分析:此处将之前的api接口进行restful风格的架构升级

//初始状态,未使用restful架构风格(有动词、驼峰命名法)
http://localhost:8081/content/findContentByCategoryId?categoryId=89

//增加版本设计风格
http://localhost:8081/api/v1/content/findContentByCategoryId?categoryId=89

//增加资源路径设计风格
http://localhost:8081/api/v1/contents/89

//增加查询参数设计风格,分页请求,排序
http://localhost:8080/api/v1/contents/89?draw=1&start=0&length=10
http://localhost:8080/api/v1/contents/89?offset=0&limit=10
http://localhost:8080/api/v1/contents/89?offset=0&limit=10&orderby={field}_asc&orderbyuser={}_desc

API文档:由后台人员根据代码提供restful风格api,移交给前端人员使用。例如:

请求地址:https://localhost:8081/contents/<category_id>
请求方式:POST
请求参数:category_id		类目	ID		必须是数字
响应结果:
[
	{
		"id":33,
		"title":"ad1",
		"subTitle":"ad1",
		"titleDesc":"ad1",
		"url":"https://sale.jd.com/act/XkCzhoisOMSW.html",
		"pic":"https://m.360buyimg.com/babel/jfs/t20164/187/1771326168/92964/b42fade7/5b359ab2N93be3a65.jpg",
		"pic2":""
	}
]

注意:1.参数表示法中,<param>表示param必填、[param]表示param选填
     2.api文档中必须明确表述前端输入、输出

代码地址Architecture-MVC-myshop 框架示例源码 tag:3.3.7-RELEASE


7.幂等性

简介:HTTP 幂等方法,是指无论调用多少次都不会有不同结果的 HTTP 方法。不管你调用一次,还是调用一百次,一千次,结果都是相同的。

HTTP GET:用于获取资源,不管调用多少次接口,结果都不会改变,所以是幂等的

GET     /tickets       # 获取ticket列表
GET     /tickets/12    # 查看某个具体的ticket

只是查询数据,不会影响到资源的变化,因此我们认为它幂等。值得注意,幂等性指的是作用于结果而非资源本身。怎么理解呢?例如,这个 HTTP GET 方法可能会每次得到不同的返回内容,但并不影响资源。可能你会问有这种情况么?当然有咯。例如,我们有一个接口获取当前时间,我们就应该设计成GET /service_time # 获取服务器当前时间,它本身不会对资源本身产生影响,因此满足幂等性。

HTTP POST:一个非幂等方法,因为调用多次,都将产生新的资源。因为它会对资源本身产生影响,每次调用都会有新的资源产生,因此不满足幂等性。

POST    /tickets       # 新建一个ticket

设计符合幂等性高质量restful api:也许,你会想起一个面试题。HTTP 请求的 GET 与 POST 方式有什么区别? 你可能会回答到:GET 方式通过 URL 提交数据,数据在 URL 中可以看到;POST 方式,数据放置在 HTML HEADER 内提交。但是,我们现在从 RESTful 的资源角度来看待问题,HTTP GET 方法是幂等的,所以它适合作为查询操作,HTTP POST 方法是非幂等的,所以用来表示新增操作。但是,也有例外,我们有的时候可能需要把查询方法改造成 HTTP POST 方法。比如,超长(1k)的 GET URL 使用 POST 方法来替代,因为 GET 受到 URL 长度的限制。虽然,它不符合幂等性,但是它是一种折中的方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值