Go后端Gin+Gorm学习笔记

API接口

接口是前后端通信的一套规范。

RESTful API

一文搞懂RESTful API - bigsai - 博客园

URL设计规范

URL为统一资源定位器 ,接口属于服务端资源,首先要通过URL这个定位到资源才能去访问,而通常一个完整的URL组成由以下几个部分构成:

1
URI = scheme "://" host  ":"  port "/" path [ "?" query ][ "#" fragment ]

scheme: 指底层用的协议,如http、https、ftp
host: 服务器的IP地址或者域名
port: 端口,http默认为80端口
path: 访问资源的路径,就是各种web 框架中定义的route路由
query: 查询字符串,为发送给服务器的参数,在这里更多发送数据分页、排序等参数。
fragment: 锚点,定位到页面的资源

我们在设计API时URL的path是需要认真考虑的,而RESTful对path的设计做了一些规范,通常一个RESTful API的path组成如下:

1
/{version}/{resources}/{resource_id}

version:API版本号,有些版本号放置在头信息中也可以,通过控制版本号有利于应用迭代。
resources:资源,RESTful API推荐用小写英文单词的复数形式。
resource_id:资源的id,访问或操作该资源。

从大体样式了解URL路径组成之后,对于RESTful API的URL具体设计的规范如下:

  1. 不用大写字母,所有单词使用英文且小写。
  2. 连字符用中杠"-"而不用下杠"_"
  3. 正确使用 "/" 表示层级关系,URL的层级不要过深,并且越靠前的层级应该相对越稳定
  4. 结尾不要包含正斜杠分隔符"/"
  5. URL中不出现动词,用请求方式表示动作
  6. 资源表示用复数不要用单数
  7. 不要使用文件扩展名

HTTP动词

在RESTful API中,不同的HTTP请求方法有各自的含义,这里就展示GET,POST,PUT,DELETE几种请求API的设计与含义分析。针对不同操作,具体的含义如下:

1
2
3
4
5
GET /collection:从服务器查询资源的列表(数组)
GET /collection/resource:从服务器查询单个资源
POST /collection:在服务器创建新的资源
PUT /collection/resource:更新服务器资源
DELETE /collection/resource:从服务器删除资源

在非RESTful风格的API中,我们通常使用GET请求和POST请求完成增删改查以及其他操作,查询和删除一般使用GET方式请求,更新和插入一般使用POST请求。从请求方式上无法知道API具体是干嘛的,所有在URL上都会有操作的动词来表示API进行的动作,例如:query,add,update,delete等等。

状态码和返回数据

服务端处理完成后客户端也可能不知道具体成功了还是失败了,服务器响应时,包含状态码返回数据两个部分。

状态码

我们首先要正确使用各类状态码来表示该请求的处理执行结果。状态码主要分为五大类:

1xx:相关信息
2xx:操作成功
3xx:重定向
4xx:客户端错误
5xx:服务器错误

每一大类有若干小类,状态码的种类比较多,而主要常用状态码罗列在下面:

200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

返回结果

针对不同操作,服务器向用户返回数据,而各个团队或公司封装的返回实体类也不同,但都返回JSON格式数据给客户端。

总结

RESTful风格的API 固然很好很规范,但大多数互联网公司并没有按照或者完全按照其规则来设计,因为REST是一种风格,而不是一种约束或规则,过于理想的RESTful API 会付出太多的成本。

比如RESTful API也有一些缺点

  • 比如操作方式繁琐,RESTful API通常根据GET、POST、PUT、DELETE 来区分操作资源的动作,而HTTP Method 本身不可直接见,是隐藏的,而如果将动作放到URL的path上反而清晰可见,更利于团队的理解和交流。
  • 并且有些浏览器对GET,POST之外的请求支持不太友好,还需要特殊额外的处理。
  • 过分强调资源,而实际业务API可能有各种需求比较复杂,单单使用资源的增删改查可能并不能有效满足使用需求,强行使用RESTful风格API只会增加开发难度和成本。

所以,当你或你们的技术团队在设计API的时候,如果使用场景和REST风格很匹配,那么你们可以采用RESTful 风格API。但是如果业务需求和RESTful风格API不太匹配或者很麻烦,那也可以不用RESTful风格API或者可以借鉴一下,毕竟无论那种风格的API都是为了方便团队开发、协商以及管理,不能墨守成规。


MVC

应用分层

应用分层是一种软件开发设计思想,将应用程序划分成N个层次,每个层次都分别负责自己的职责,多个层次之间来协同提供完整的功能,根据项目的复杂度,将项目分成三层或四层等。

在功能简单,代码量少是,我们通常不考虑分层,但是,随着业务越来越复杂,功能越来越强大,大量的代码都混在了一起,会出现逻辑不清晰,各模块相互依赖,代码扩展性差,改动一处牵动全身等问题,所以,就要对项目进行分层,MVC 和 三层架构 都是应用分层的充分体现,下面先看一下什么是MVC。

什么是MVC?

MVC(Model View Co ntroller),它是一种思想,旨在将应用程序的关注点分离,提高代码的可维护性。他把软件系统分为以下三部分:

Model(模型):用来处理程序中数据逻辑的部分

View(视图):在应用程序中,专门和浏览器进行交互,展示数据的资源

Contreller(控制器):可以理解成是一个分发器,来决定对于视图发来的请求,需要用哪一个模型来处理,以及处理完后需要跳回到哪一个视图,也就是用来连接视图和模型的

比如我们去餐厅吃饭,服务员就会来接待我们,服务员就会将我们点的菜写在小本本上,然后交给前台,前台再交给厨师,厨师做完之后,就会再交给前台,前台再根据这个菜确定是哪个菜单,然后再让服务员交给客人,此时,服务员帮我们写菜单就相当于是一个视图,前台就相当于是控制器,厨师就相当于是模型

三层架构

现在MVC这种方式也已经不再使用了,而主流的是前后端分离,不再需要View这个模块了,不需要再关注于前端了,我们只要约定好接口,写好后端即可,所以,对于后端,也有了一种新的分层方式,就是三层架构,分为以下三层:

  1. 表现层:展示数据结果,和接收用户的请求

  2. 业务逻辑层:负责处理业务逻辑

  3. 数据层:负责存储和管理数据

按照这样的层次划分,Spring MVC 站在后端的角度上,就将代码分成了:

  1. Controller 层:控制层,用来接收前端发来的请求,在Service 层中选择对应的处理逻辑,并且给前端进行响应
  2. Service 层:业务逻辑层,对发来的请求进行具体的逻辑处理
  3. Dao 层:数据访问层,负责访问数据库,进行增删查改的操作

HTTP请求

http 请求报文包含三个部分(请求行 + 请求头 + 请求体)

请求行(Request Line)

请求行包含三个内容:

method (GET/POST等)

request-URI (http://www.example.com/api/v1/test)

HTTP-version (HTTP1.1/HTTP2.0)

请求头(Request Header)

请求头由 key/value 对,也就是键值对组成,每行为一对。

常见的请求字段:

Authorization,用于对应地认证信息。

Content-Type,指明请求体(Body)的内容格式(application/json:请求体是JSON格式,application/x-www-form-urlencoded:表单数据,键值对形式,multipart/form-data:用于文件上传,text/plain:纯文本格式)。

Uesr-Agent,标识客户端的软件名称、版本、操作系统等信息(User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36)。

Accept,指明客户端能够处理的响应内容类型(application/json:期望响应是JSON格式,text/html:期望响应是HTML页面,*/*:表示接受任何类型的响应)。

Cache-Control,控制缓存行为(no-cache:强制服务器返回最新的响应,no-store:禁止缓存,max-age=<seconds>:设置缓存的最大有效期)。

Host,指定请求的目标主机和端口号。

Accept-Encoding,指明客户端支持的压缩格式(gzip:支持gzip压缩,deflate:支持deflate压缩,br:支持Brotli压缩)。

Connection,控制连接的行为(keep-alive:保持连接,close:关闭连接)。

Referer,指明请求来源页面的URL(Referer: https://www.example.com/page)。

Origin,在跨域请求(CORS)中,标识请求的来源域(Origin: https://example.com)。

Content-Length,指明请求体的长度(以字节为单位)。

If-Modifed-Since,如果资源自指定时间后未被修改,则返回304状态码(If-Modified-Since: Mon, 01 Jan 2024 00:00:00 GMT)。

If-None-Match,与服务器上的资源版本进行比较,如果匹配则返回304状态码(If-None-Match: "123456789")。

请求体(Request Body)

请求体(Request Body) 是 HTTP 请求中用于携带客户端向服务器发送的数据的部分。它通常用于 POST、PUT 和 PATCH 请求,因为这些请求通常需要向服务器发送额外的信息(如表单数据、文件或 JSON 数据)。

请求体用于向服务器发送数据,这些数据可以是:

  • 用户提交的表单信息(如用户名、密码)。
  • 文件上传(如图片、文档)。
  • JSON 格式的结构化数据(如 API 请求)。
  • XML 或其他自定义格式的数据。

HTTP响应

HTTP响应也由行、头、体组成。

状态行

状态行包括:

HTTP-version(HTTP1.1/HTTP2.0)

状态码(200)

状态码的文本描述(OK)

响应头

响应头也由 key/value 对组成,每行为一对。

常见的响应字段:

Content-Type,指定响应体的内容格式(text/html:HTML 页面,application/json:JSON 格式数据,application/xml:XML 格式数据,image/pngimage/jpeg:图片格式,text/plain`:纯文本)。

Content-Length,指定响应体的长度(以字节为单位)。

Cache-Control,控制响应的缓存策略(max-age=<seconds>:设置响应的最大缓存时间,no-cache:强制客户端在使用缓存前验证资源是否更新,no-store:禁止缓存,public:允许响应被缓存,private:仅允许单个用户缓存)。

响应体

当 Web 服务器接受到 web 客户端的请求报文后,对 HTTP 请求报文进行解析,将 Web 客户端的请求的对象取出打包,通过 HTTP 响应报文将数据传回给客户端;若出现错误,则返回包含对应错误的错误代码和错误原因。


JWT定义

JSON Web Token(JWT | json 网络令牌)是一种开放标准(RFC 7519),用于在网络应用环境间安全地传递声明。

JWT是一种紧凑且自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。由于其信息是经过数字签名地,所以可以确保发送地数据在传输过程中未被篡改。

JWT组成部分

JWT由三个部分组成,Header(头部),Payload(载荷),Signature(签名)(xxxxx.yyyyy.zzzzz)。

Header(头部)

Header 头部案例:

1
2
3
4
{
"alg": "HS256"
"typ": "JWT"
}

进行 Base64 加密(可解密),构成了第一部分

Payload(载荷)

Payload 部分包含所传递地声明(Claims)。声明有三种:

注册声明:这些声明是预定义的,非必须使用的但被推荐使用。官方标准定义的注册声明如 exp,即为过期时间

公共声明:JWT 签发方可以自定义的声明,如 username

私有声明:JWT 签发方因为项目需要而自定义的声明,更符合实际项目场景,区别于注册声明公共声明

然后进行 Base64 加密,得到 JWT 的第二部分

Signature(签名)

用于验证消息在此过程中没有更改。

JWT 的使用

在请求头里添加 Authorization,并加上 Bearer 前缀


CRUD 简述

CRUD 代表 Create(创建)、Read(读取)、Update(更新)和 Delete(删除)。

在计算机程序设计中,CRUD 是对数据进行的一系列基本操作。

中间件

中间件是为了过滤路由而发明的一种机制,也就是 http 请求来到时先经过中间件,再到具体的处理函数。