ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

在 Go 中实现 REST 架构:基于 net/http 与 httprouter 的实战指南

在 Go 中实现 REST 架构:基于 net/http 与 httprouter 的实战指南 文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载RESTREpresentational State Transfer是当下互联网上最流行的软件架构风格它以资源为中心、充分复用 HTTP 协议并以无状态与系统分层为设计基石。本篇技术指南以《build-web-application-with-golang》电子书第 8.3 节en/08.3.md为核心先讲透 REST 的三大核心概念与架构约束再通过完整的 Go 代码示例演示如何借助net/http与第三方路由库github.com/julienschmidt/httprouter为同一资源的不同 HTTP 方法GET/POST/PUT/DELETE绑定不同处理逻辑从而构建真正 RESTful 的 Web 应用。读完本文你将掌握 REST 架构的判定标准、REST 成熟度分级以及一套可直接复制运行的 Go REST 接口实现方案。REST 是什么三个必须理解的核心概念REST 概念首次出现于 2000 年 Roy Thomas Fielding 的博士论文中——Fielding 同时也是 HTTP 协议规范的主要编写者之一。REST 指的是一组架构约束条件和原则任何满足这些约束条件和原则的应用程序或设计都可以被称为 RESTful。要真正理解 REST需要先理解以下三个概念。资源ResourcesREST 是表现层状态转化的缩写它其实省略了主语——表现层指的是资源的表现层。那么什么是资源我们平时上网访问的一张图片、一个文档、一个视频等都是资源的例子。这些资源通过 URI 来定位也就是说每一个 URI 就代表一种资源。URI 是资源的唯一标识符也是 REST 架构中以资源为中心这一理念的直接体现。表现层Representation资源是一个具体的实体信息它可以有多种展现方式而把实体展现出来就是表现层。例如一个 TXT 文本信息可以输出为 HTML、JSON、XML 等格式一张图片可以用 JPG、PNG 等方式展现——这就是表现层的含义。URI 确定了一个资源但如何确定它的具体表现形式呢答案在 HTTP 请求的头信息中Accept和Content-Type字段用于描述表现层。Accept声明客户端期望接收的格式如application/jsonContent-Type声明请求体实际使用的媒体类型这两个字段共同决定了资源在客户端与服务器之间以何种表现进行传递。状态转化State Transfer访问一个网站就代表了客户端和服务器之间的一次互动过程。在这个过程中必然涉及数据和状态的变化。而 HTTP 协议本身是无状态的——这意味着这些状态必须保存在服务器端当客户端想要通知服务器端改变数据和状态时必须通过某种方式来告知它。客户端能通知服务器端的手段只能是 HTTP 协议具体来说是 HTTP 协议中四个表示操作方式的动词它们分别对应四种基本操作HTTP 方法操作语义GET获取资源POST新建资源也可以用于更新资源PUT更新资源DELETE删除资源综合总结RESTful 架构的三条判据综合上面的解释可以总结出 RESTful 架构的完整定义每一个 URI 代表一种资源客户端和服务器之间传递这种资源的某种表现层客户端通过四个 HTTP 动词对服务器端资源进行操作实现表现层状态转化。REST 的两大架构原则无状态与系统分层一个 Web 应用要满足 REST最重要的原则是客户端和服务器之间的交互在请求之间是无状态的。也就是说从客户端到服务器的每个请求都必须包含理解该请求所必需的全部信息。这一原则带来三个直接收益如果服务器在请求之间的任何时间点重启客户端不会得到通知也无需关心——因为每个请求都是自包含的同一个请求可以由任何一台可用的服务器来应答这非常适合云计算之类的多副本环境因为无状态客户端可以缓存数据来改进性能。另一个重要的 REST 原则是系统分层delamination。它表示组件无法了解除了与它直接交互的层次以外的其他组件。通过将系统知识限制在单个层内可以限制整个系统的复杂性从而促进底层组件的独立性。下图即是书中给出的 REST 架构图en/images/8.3.rest2.png当 REST 的约束条件作为一个整体应用时将生成一个可以扩展到大量客户端的应用程序同时还能降低客户端与服务器之间的交互延迟统一接口简化了整个系统的架构改进子系统之间交互的可见性。下图展示了 REST 的扩展性——通过负载均衡器、并行 API 前端节点与服务协调器的分层设计实现水平扩展en/images/8.3.rest.pngRESTful 的实现Go 中如何落地Go 并没有为 REST 提供直接的语法支持但由于 RESTful 应用本质上是基于 HTTP 协议实现的因此可以利用标准库net/http包自己实现当然需要针对 REST 做一定的改造REST 的核心要求是根据不同的 HTTP 方法method来处理相应的资源即同一资源、不同方法、不同逻辑。REST 成熟度的三个 level目前已经存在很多自称是 REST 的应用其实并没有真正实现 REST。书中将这些应用根据所实现的 HTTP 方法分为几个级别如下图所示en/images/8.3.rest3.png上图展示了目前实现 REST 的三个 level从仅实现 GET到实现 GET 与 POST再到完整覆盖 GET、POST、PUT、DELETE乃至 PATCH。需要说明的是在应用开发时并不一定需要完全按照 RESTful 的规则实现全部方式因为有些时候完全 RESTful 的方式未必可行——RESTful 服务要求充分利用每一个 HTTP 方法包括DELETE和PUT但现实中有两类典型限制HTML 标准的限制HTML 标准只能通过链接和表单支持GET和POST。在没有 Ajax 支持的网页浏览器中无法发出PUT或DELETE命令防火墙的限制有些防火墙会拦截 HTTPPUT和DELETE请求。要绕过这个限制客户端需要把实际的PUT和DELETE请求通过 POST 请求穿透过来而 RESTful 服务则要负责在收到的 POST 请求中找到原始的 HTTP 方法并还原。一种常用的折中方案是在 POST 请求中增加一个隐藏字段_method来模拟PUT、DELETE等方式但服务器端需要对它做转换。不过在 Go 语言中完全按照 RESTful 规范来实现其实是很容易的下面通过完整的例子来说明如何实现 RESTful 的应用设计。基于 httprouter 的 REST 示例完整代码书中给出的示例代码如下原文档见 en/08.3.mdpackage main import ( fmt log net/http github.com/julienschmidt/httprouter ) func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { fmt.Fprint(w, Welcome!\n) } func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { fmt.Fprintf(w, hello, %s!\n, ps.ByName(name)) } func getuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid : ps.ByName(uid) fmt.Fprintf(w, you are get user %s, uid) } func modifyuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid : ps.ByName(uid) fmt.Fprintf(w, you are modify user %s, uid) } func deleteuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { uid : ps.ByName(uid) fmt.Fprintf(w, you are delete user %s, uid) } func adduser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { // uid : r.FormValue(uid) uid : ps.ByName(uid) fmt.Fprintf(w, you are add user %s, uid) } func main() { router : httprouter.New() router.GET(/, Index) router.GET(/hello/:name, Hello) router.GET(/user/:uid, getuser) router.POST(/adduser/:uid, adduser) router.DELETE(/deluser/:uid, deleteuser) router.PUT(/moduser/:uid, modifyuser) log.Fatal(http.ListenAndServe(:8080, router)) }代码逐层拆解从路由注册到方法分发这段示例演示了如何编写一个最基本的 REST 应用。我们访问的资源是用户user针对同一资源的不同操作通过不同的 HTTP 方法映射到不同的处理函数router.GET(/user/:uid, getuser)——获取指定 uid 的用户router.POST(/adduser/:uid, adduser)——新增用户router.DELETE(/deluser/:uid, deleteuser)——删除用户router.PUT(/moduser/:uid, modifyuser)——修改用户。其中:uid是路径参数动态路由在处理器中通过ps.ByName(uid)取出具体的参数值——这正是 REST 每个 URI 代表一种资源 在实现层面的落地/user/:uid这类带参数的路由模式让同一个资源模板可以覆盖任意具体的资源实例。代码中使用了第三方库github.com/julienschmidt/httprouter。在本书前面的章节如第 13.2 节 自定义路由器介绍过如何实现自定义的路由器而httprouter恰恰实现了自定义路由和方便的路由规则映射——它的核心价值在于将 HTTP 方法GET/POST/PUT/DELETE与 URL 路径绑定到特定的处理器函数这正是实现 RESTful 架构最关键的方法分发能力。相比之下Go 标准库默认的DefaultServeMux即http.Handle/http.HandleFunc注册的默认路由器见 en/03.4.md存在两个与 REST 直接相关的局限不支持带参数的动态路由如the/user/:UID这类模式对 REST 的支持不够好——访问方法无法被限制例如用户访问/foo时GET、POST、DELETE、HEAD 等 HTTP 方法都可能被接受。而httprouter通过方法化的路由注册router.GET(...)、router.POST(...)、router.PUT(...)、router.DELETE(...)彻底解决了这两个问题同一个路径模板可以按不同方法注册不同的处理器未注册的方法将不会被路由这也正是 REST 要求对同一资源的不同方法实现不同逻辑的天然映射。运行与验证将上述代码保存为main.go通过go get github.com/julienschmidt/httprouter获取依赖后运行go run main.go服务会监听:8080端口。此时可以用curl直接验证 REST 接口# 获取资源GET curl http://localhost:8080/user/123 # 输出: you are get user 123 # 新建资源POST curl -X POST http://localhost:8080/adduser/456 # 输出: you are add user 456 # 更新资源PUT curl -X PUT http://localhost:8080/moduser/789 # 输出: you are modify user 789 # 删除资源DELETE curl -X DELETE http://localhost:8080/deluser/321 # 输出: you are delete user 321这个例子也印证了 REST 的本质REST 就是根据不同的 method 访问同一个资源的时候实现不同的逻辑处理。实际工程中处理器内部通常会进一步调用数据库层完成真实的增删改查可参考本书第 5 章 数据库 的相关实现并基于Accept/Content-Type头协商 JSON 或 XML 等表现层格式第 7 章 JSON 与 XML 提供了 Go 侧的处理细节。总结REST 是一种架构风格它汲取了 WWW 的成功经验无状态、以资源为中心、充分利用 HTTP 协议和 URI 协议、提供统一的接口定义这使得它作为一种 Web 服务设计方法而广泛流行。在某种意义上通过强调 URI 和 HTTP 等早期 Internet 标准REST 是对大型应用程序服务器时代之前 Web 方式的回归并为大型、可扩展的 Web 应用铺平了道路。目前 Go 对 REST 的支持还比较简单直接通过实现自定义的路由规则为不同的 method 实现不同的 handler即可实现 REST 的架构。无论是使用httprouter这类第三方库还是像本书第 13 章那样从零构建自己的路由与控制器核心思路始终一致——以 URI 标识资源、以 HTTP 方法表达操作、以无状态设计支撑水平扩展。延伸阅读本章目录第 8 章 Web 服务上一节WebSocket下一节RPC全书目录preface.md相关主题自定义路由器实现 13.2 自定义路由赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐HttpRouter 源码级实战指南基于压缩基数树的高性能 Go HTTP 请求路由器HttpRouter 源码级实战指南基于压缩基数树的高性能 Go HTTP 请求路由器 导读 HttpRouter 是一个轻量级、高性能的 Go HTTP 请后端深入解析 Kiota HTTP Library for Go基于 net/http 的请求适配器与中间件管道实战指南深入解析 Kiota HTTP Library for Go基于 net/http 的请求适配器与中间件管道实战指南 本指南围绕 OpenShift 测试套件测试云原生质量保障OpenCloud 中的 Go HTTP/2 实现解析golang.org/x/net/http2 的双实现架构与 Go 1.27 标准库迁移OpenCloud 中的 Go HTTP/2 实现解析golang.org/x/net/http2 的双实现架构与 Go 1.27 标准库迁移 导读 本篇文章后端微服务存储认证鉴权上一篇YouTube.js 通知节点解析深入理解 NotificationAction 节点与 response_text 响应文本下一篇Halo 分类父级编辑支持在分类编辑弹窗中调整父分类的实现详解创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表