mirror of
				https://github.com/ZSCNetSupportDept/website.git
				synced 2025-10-31 02:16:19 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			157 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # 基于HTTP的Web后端的组成
 | ||
| 一个基于HTTP的Web后端通常有以下部分组成:
 | ||
| 
 | ||
| ##### 路由系统(router)
 | ||
| 
 | ||
| 路由系统负责处理用户访问网页时的请求路径/方法,并转交给对应的处理者
 | ||
| 
 | ||
| 
 | ||
| ##### 处理者(handler)
 | ||
| 
 | ||
| 处理者负责处理用户的请求,读取用户在URI中的参数,和请求体中的内容(如果有)等,统称为上下文(Context),负责返回请求所对应的回应
 | ||
| 
 | ||
| 有的系统还会继续细分,将业务层和接口层分开(这种情况下通常接口层是和路由功能合并的)
 | ||
| 
 | ||
| 
 | ||
| _最低要求是这个,另外,通常一个后端系统还需要连接一个数据库:_
 | ||
| 
 | ||
| 
 | ||
| ##### 数据库
 | ||
| 
 | ||
| 通常是兼容SQL协议的关系型数据库,负责存储后端所需要用到和产生的信息
 | ||
| 
 | ||
| _其实很多后端系统无非就是对数据库的增删改查(所谓的CRUD),可以说这些系统就是数据库的一层方便wrapper_
 | ||
| 
 | ||
| 
 | ||
| ##### 鉴权系统
 | ||
| 
 | ||
| 通常,我们系统的内容不打算对互联网上的任何一个人开放,所以我们需要一些方法来验证访问者的身份
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| ##### 模板系统(optional)
 | ||
| 
 | ||
| 如果你打算通过后端渲染HTML返回到用户浏览器,那你需要一套模板来方便地将动态内容插入到模板里面返回给用户,如果是一个纯粹API的站点,还是想直接把工作甩给前端,你就可以不用配置模板
 | ||
| 
 | ||
| 
 | ||
| 此外,还有一些外围的工作:
 | ||
| 
 | ||
| ##### 反向代理
 | ||
| 
 | ||
| 通常我们的Web后端服务不是直接暴露对外访问的,而是经过一层代理的转发,这样更加的安全,配置也更加简单,服务只需要监听本地端口
 | ||
| 
 | ||
| ##### 配置系统
 | ||
| 
 | ||
| 你的系统需要读取配置,比如监听端口,数据库连接,还有其他服务的密钥等
 | ||
| 
 | ||
| ##### CI/CD
 | ||
| 
 | ||
| 自动化配置构建,部署,测试等工作,让你专注代码工作,而不用把心思过多地放在部署构建这些工作上面
 | ||
| 
 | ||
| ## 路由
 | ||
| 假设你的一个报名服务架设在`service.io`上:
 | ||
| 
 | ||
| 首先,用户访问这个网址时需要显示一段欢迎文字,然后将他们引导到报名的页面
 | ||
| 
 | ||
| 那么,你应该在用户访问`/`时返回一个html文件,里面含有导向`/volunteer.html`的超链接
 | ||
| 
 | ||
| `/volunteer.html`里面的前端代码需要以AJAX的形式与后端API交互,比如:
 | ||
| 
 | ||
| `service.io/api/register`接受POST请求,前端上传报名人的信息,后端录入数据库,并返回录入的信息,全部以JSON序列化
 | ||
| 
 | ||
| `service.io/api/view`接受GET请求,让这个报名人查看自己的报名信息,返回JSON格式
 | ||
| 
 | ||
| `service.io/api/cancel`接受POST请求,取消某个报名人的信息,成功则返回相应状态码
 | ||
| 
 | ||
| `service.io/admin/viewAll`让管理员查看当前的所有报名,接受GET请求
 | ||
| 
 | ||
| `service.io/admin/cancel`让管理员取消任意的报名,接受的POST
 | ||
| 
 | ||
| 在现代网站设计中,我们不是在根目录下面创建对应的文件(实际上,连根目录都不需要了),我们使用一些叫做“路由器(router)”的模块,当用户通过一定的方法请求一定的路径时,就把这些请求转交到相应的handler
 | ||
| ### URI参数
 | ||
| 参数在正常路径后面,以?开始,以&分割,以键值对的形式存在
 | ||
| 
 | ||
| 例如:`service.io/api/register?name=小明&phone=10000000000&freeday=2024-9-25`
 | ||
| 
 | ||
| 这样的参数可以被许多后端框架使用内置的解析器解析
 | ||
| 
 | ||
| ### 参数化路径
 | ||
| 这种路径通常是配合REST风格的接口来设计的,比如:
 | ||
| 
 | ||
| `service.io/api/users/小明`
 | ||
| 
 | ||
| 类似于这样的路径,许多后端框架可以使用`service.io/api/users/:user`这样的形式来匹配,在转交给的handler中可以读取`:user`参数,从而返回参数所指定的资源
 | ||
| ### API设计
 | ||
| API的设计包括了路径的设计和接口格式的设计,一般小项目可以相对地随便一点,但是大项目还是需要认真一点的
 | ||
| #### 传统
 | ||
| 我们刚才所举的例子就是一个传统的API设计,一个路径就对应了一个业务点,一般只会使用`GET`和`POST`来对应获取和上传,前后端的交互一般在文档里自行约定,或者采用内置的表单(如果数据比较简单的话)
 | ||
| #### REST
 | ||
| REST的意思是“表现层状态转换”(英语:Representational State Transfer,缩写:REST),这种风格的要点是一个URI表示一个资源,而不是一个业务,同时充分地利用HTTP方法
 | ||
| 
 | ||
| 例如,我们在前面定义了`Register`,`View`和`Cancel`三个API,如果是要上传什么就用`POST`,获取`GET`,一个路径表示的是一个业务,而不是系统的某个资源,下面来看看REST怎么写:
 | ||
| 
 | ||
| `POST service.io/api/users/小明` 用户小明提交一个报名,具体的报名信息在请求体里,这将在数据库里面创建一个小明的报名记录
 | ||
| 
 | ||
| `GET service.io/api/users/小明` 用户小明查看自己的报名信息
 | ||
| 
 | ||
| `DELETE service.io/api/users/小明` 取消小明的报名
 | ||
| 
 | ||
| `PUT service.io/api/users/小明` 修改小明的报名信息,新信息放在请求体里面了
 | ||
| 
 | ||
| 可以发现,REST风格的API可以看作是对传统静态网页互联网的回归,这种风格直观简洁,兼容性更好,更加利于缓存等
 | ||
| 
 | ||
| #### GraphQL
 | ||
| GraphQL是一种用于API交互的查询语言,他意图解决接口格式定义和多次查询带来的复杂问题
 | ||
| 
 | ||
| 首先,后端需要支持GraphQL,然后,前端需要在API请求中注明自己想后端用什么格式呈现什么想要的信息,这样就不需要前端多次请求不同的业务了
 | ||
| 
 | ||
| 这对大型系统或许比较友好,但是如果只是一些小项目的话,这可能有些复杂,具体可以自己去了解
 | ||
| ## handler
 | ||
| handler可以说是一个后端系统的核心了,因为他们是实际处理业务的地方
 | ||
| ### 面向对象与模型
 | ||
| 虽然可以使用其他的范式,但是最推荐的是依据OOP的原则,将需要处理的模型写成对象,将一系列操作写成对象的方法
 | ||
| 例如,报名系统本质上就是处理“报名人”这个对象的各种操作,我们可以定义:
 | ||
| 
 | ||
| ```Go
 | ||
| 
 | ||
| type Volunteer struct{
 | ||
|     config.DB
 | ||
|     id      int
 | ||
|     Name    string
 | ||
|     Phone   int
 | ||
|     FreeDay time.Time
 | ||
|     Note    string
 | ||
| }
 | ||
| 
 | ||
| func (v *volunteer)Add()error{
 | ||
|     if err:=db.addVolunteer(v.MainConnection) err!=nil{
 | ||
|         return err
 | ||
|     }
 | ||
|     return nil
 | ||
| }
 | ||
| 
 | ||
| ```
 | ||
| 要设计一个登记报名的handler,就只需要将前端发过来的信息反序列化到Volunteer对象里,然后调用Add方法即可,这种思路就叫做面向对象
 | ||
| 
 | ||
| 所设计的Volunteer和他的一系列方法就叫做“模型”
 | ||
| 
 | ||
| ## 数据库
 | ||
| 数据库为后端的业务数据提供了方便的存储和查询服务,这里指的是结构化数据,其他需求可以去看高级教程
 | ||
| ### SQLite
 | ||
| 这是个轻量级的数据库,一个数据库就是一个文件,通常用于业务量比较小的场景或者是本地开发的场景
 | ||
| ### PostgreSQL
 | ||
| 这个可以说是目前最厉害的开源数据库了,不仅提供了常规的结构化功能,而且对于一些简单的非结构化存储需求也可以胜任,但是本身的资源消耗是有点高的
 | ||
| 
 | ||
| ### 选择数据库的各种考量
 | ||
| - 业务量:如果业务量比较小的话,就使用SQLite吧
 | ||
| - 兼容性:如果现有的环境和业务依赖于某个数据库,那么就继续用吧
 | ||
| ### ORM
 | ||
| ORM是对数据库的一层抽象,让你专注于业务逻辑的开发,而不用担心具体的数据库问题,如果对于性能或者其他的方面没有很大的需求的话,建议在你的项目里使用ORM
 | ||
| ## 鉴权
 | ||
| ### Session
 | ||
| ### JWT
 | ||
| ### OAuth2.0
 | ||
| 
 | ||
| ## 模板
 | ||
| ### Jinja2
 |