Web 后端开发1—协议和通信 & 架构设计(后端示例:Go)

Web 后端开发—索引目录

一、协议和通信

当我们在浏览器中输入一个网址并按下回车键时,浏览器会向服务器发送一个HTTP请求,服务器接收到请求后会返回一个HTTP响应。

1、HTTP协议

HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议,通常用于在客户端和服务器之间进行通信。

1.1 HTTP协议概述

  1. 协议基础: HTTP是一种无状态协议,每个请求都是独立的,服务器不会记住之前的请求。客户端通过发送请求,服务器通过发送响应进行通信。

  2. 通信流程: 通常,HTTP通信涉及客户端发送请求给服务器,服务器处理请求并返回相应的响应。请求和响应都包含头部信息和可选的主体(body)。

1.2 请求和响应

HTTP请求示例

客户端向服务器发出HTTP请求,请求中包含有关所需资源的信息。

简易HTTP GET请求示例:

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
  • GET /index.html HTTP/1.1: 表示请求的方法是GET,请求的资源是/index.html,使用的HTTP版本是1.1。
  • Host: www.example.com: 指定请求的目标主机。
  • User-Agent: 标识了发起请求的用户代理,通常是浏览器信息。
  • Accept: 告诉服务器可以接受的响应的内容类型。
HTTP响应示例

服务器接收到请求后,会返回相应的响应。

简易HTTP响应示例:

HTTP/1.1 200 OK
Date: Sat, 11 Dec 2023 15:12:53 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1234

<!DOCTYPE html>
<html>
<head>
    <title>Example Page</title>
</head>
<body>
    <h1>Hello, World!</h1>
</body>
</html>
  • HTTP/1.1 200 OK: 表示响应的HTTP版本和状态码(200表示成功)。
  • Date: 响应的日期和时间。
  • Content-Type: 指定了响应主体的类型和字符集。
  • Content-Length: 指定了响应主体的长度。
示例结果

客户端发送请求,服务器收到请求并返回响应。

在这个例子中,服务器成功响应,返回一个包含HTML内容的页面。

1.3 请求方法

当谈到Web后端开发时,了解HTTP请求方法(GET、POST、PUT、DELETE等)是至关重要的,因为它们定义了客户端与服务器之间的交互方式。以下是对每种请求方法的简要解释和一些详细的示例:

GET 请求方法
  • 用途: 用于从服务器获取数据,通常用于请求页面、图片或其他资源。
  • 示例:
    GET /api/users/123 HTTP/1.1
    Host: example.com
    
  • 结果: 服务器会返回ID为123的用户信息。
POST 请求方法
  • 用途: 用于向服务器提交数据,通常用于创建新资源或提交表单数据。
  • 示例:
    POST /api/users HTTP/1.1
    Host: example.com
    Content-Type: application/json
    Content-Length: 45
    
    {"name": "John", "email": "john@example.com"}
    
  • 结果: 服务器会创建一个新用户,姓名为John,邮箱为john@example.com。
PUT 请求方法
  • 用途: 用于向服务器更新数据,通常用于更新现有资源。
  • 示例:
    PUT /api/users/123 HTTP/1.1
    Host: example.com
    Content-Type: application/json
    Content-Length: 48
    
    {"name": "John Doe", "email": "john.doe@example.com"}
    
  • 结果: 服务器会更新ID为123的用户信息,将姓名改为John Doe,邮箱改为john.doe@example.com。
DELETE 请求方法
  • 用途: 用于从服务器删除数据,通常用于删除资源。
  • 示例:
    DELETE /api/users/123 HTTP/1.1
    Host: example.com
    
  • 结果: 服务器会删除ID为123的用户信息。

1.4 状态码

HTTP状态码是服务器对客户端请求的响应的一部分,它们指示了请求的处理状态。

1xx - 信息性状态码
  • 100 Continue: 表示服务器已经接收到请求的初始部分,客户端应该继续发送剩余的请求。
  • 解释: 当客户端发送带有大量数据的请求时,服务器可能会返回此状态码,以指示客户端可以继续发送数据。
2xx - 成功状态码
  • 200 OK: 表示请求已成功被服务器处理。
  • 解释: 当客户端请求一个页面时,服务器成功返回页面内容。
3xx - 重定向状态码
  • 301 Moved Permanently: 表示请求的资源已被永久移动到新位置。
  • 解释: 当访问一个网页时,如果该页面已经被永久移动到新的URL,服务器会返回此状态码,并在响应头中包含新的URL。
4xx - 客户端错误状态码
  • 400 Bad Request: 表示服务器无法理解客户端发送的请求,通常是因为请求语法错误。

  • 解释: 当客户端发送一个无效的请求时,服务器会返回此状态码。

  • 404 Not Found: 表示请求的资源未被找到。

  • 示例: 当客户端请求一个不存在的页面时,服务器会返回此状态码。

5xx - 服务器错误状态码
  • 500 Internal Server Error: 表示服务器在处理请求时发生了意外错误。
  • 解释: 当服务器端代码出现错误时,服务器会返回此状态码。

2、RESTful API设计

2.1 资源定义和标识

当设计RESTful API时,关键的一步是定义资源和标识它们。

RESTful API的核心理念是将应用程序的功能映射到一组定义清晰、易于理解和操作的资源上。

资源定义

在RESTful API中,资源是应用程序中的实体或对象,它可以是任何东西,比如用户、文章、评论等。资源通常通过URI(统一资源标识符)来标识。

示例: 假设我们正在设计一个博客应用,我们可以定义文章(Post)作为一个资源。

// 定义文章结构体
type Post struct {
    ID      int    `json:"id"`
    Title   string `json:"title"`
    Content string `json:"content"`
}

// 假设我们有一组文章
var posts = []Post{
    {ID: 1, Title: "RESTful API Design", Content: "Learn how to design RESTful APIs."},
    {ID: 2, Title: "Golang Basics", Content: "Introduction to Go programming language."},
}
资源标识

每个资源都有一个唯一的标识符,通常是通过URI表示。标识符应该具有一致性和可读性。

示例: 对于文章资源,我们可以使用以下URI来标识单个文章和文章列表:

// 单个文章的URI
/posts/{id}

// 文章列表的URI
/posts
示例代码

这个示例展示了如何设计RESTful API,定义资源(文章)和标识资源的URI,以及如何处理不同的HTTP请求以返回相应的结果。

我们要实现获取所有文章和获取单个文章的功能,可以参照这种写法:

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
)

// 处理获取所有文章的请求
func getAllPosts(w http.ResponseWriter, r *http.Request) {
    // 将文章列表转换为JSON格式并发送给客户端
    json.NewEncoder(w).Encode(posts)
}

// 处理获取单个文章的请求
func getPostByID(w http.ResponseWriter, r *http.Request) {
    // 从请求参数中获取文章ID
    params := r.URL.Query()
    id, err := strconv.Atoi(params.Get("id"))
    if err != nil {
        http.Error(w, "Invalid ID", http.StatusBadRequest)
        return
    }

    // 查找文章
    var foundPost *Post
    for _, post := range posts {
        if post.ID == id {
            foundPost = &post
            break
        }
    }

    // 如果找到文章,将其转换为JSON格式并发送给客户端,否则返回404 Not Found
    if foundPost != nil {
        json.NewEncoder(w).Encode(foundPost)
    } else {
        http.NotFound(w, r)
    }
}

func main() {
    // 设置路由
    http.HandleFunc("/posts", getAllPosts)
    http.HandleFunc("/post", getPostByID)

    // 启动服务器
    fmt.Println("Server is running on :8080")
    http.ListenAndServe(":8080", nil)
}
解释
  1. 获取所有文章: 发送GET请求到/posts,服务器会返回包含所有文章的JSON数组。

  2. 获取单个文章: 发送GET请求到/post?id=1,服务器会返回ID为1的文章的JSON表示。

2.2 CRUD操作

CRUD(Create、Read、Update、Delete)。这些操作代表了对资源的基本操作,包括创建、读取、更新和删除。

创建资源 (Create - POST)

创建资源是指向服务器提交数据以创建新资源的过程。

示例: 创建新的文章:

// 处理创建文章的请求
func createPost(w http.ResponseWriter, r *http.Request) {
    // 从请求体中解析JSON数据
    var newPost Post
    err := json.NewDecoder(r.Body).Decode(&newPost)
    if err != nil {
        http.Error(w, "Invalid request body", http.StatusBadRequest)
        return
    }

    // 为新文章生成唯一ID
    newPost.ID = len(posts) + 1

    // 将新文章添加到文章列表
    posts = append(posts, newPost)

    // 返回创建的文章的JSON表示
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(newPost)
}
读取资源 (Read - GET)

读取资源是指从服务器获取资源的过程。

示例: 获取所有文章和获取单个文章的功能:

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
)

// 处理获取所有文章的请求
func getAllPosts(w http.ResponseWriter, r *http.Request) {
    // 将文章列表转换为JSON格式并发送给客户端
    json.NewEncoder(w).Encode(posts)
}

// 处理获取单个文章的请求
func getPostByID(w http.ResponseWriter, r *http.Request) {
    // 从请求参数中获取文章ID
    params := r.URL.Query()
    id, err := strconv.Atoi(params.Get("id"))
    if err != nil {
        http.Error(w, "Invalid ID", http.StatusBadRequest)
        return
    }

    // 查找文章
    var foundPost *Post
    for _, post := range posts {
        if post.ID == id {
            foundPost = &post
            break
        }
    }

    // 如果找到文章,将其转换为JSON格式并发送给客户端,否则返回404 Not Found
    if foundPost != nil {
        json.NewEncoder(w).Encode(foundPost)
    } else {
        http.NotFound(w, r)
    }
}

func main() {
    // 设置路由
    http.HandleFunc("/posts", getAllPosts)
    http.HandleFunc("/post", getPostByID)

    // 启动服务器
    fmt.Println("Server is running on :8080")
    http.ListenAndServe(":8080", nil)
}
更新资源 (Update - PUT)

更新资源是指向服务器提交数据以更新现有资源的过程。

示例: 更新文章的内容:

// 处理更新文章的请求
func updatePost(w http.ResponseWriter, r *http.Request) {
    // 从请求参数中获取文章ID
    params := r.URL.Query()
    id, err := strconv.Atoi(params.Get("id"))
    if err != nil {
        http.Error(w, "Invalid ID", http.StatusBadRequest)
        return
    }

    // 查找要更新的文章
    var foundPost *Post
    for i, post := range posts {
        if post.ID == id {
            foundPost = &posts[i]
            break
        }
    }

    // 如果找到文章,从请求体中解析JSON数据并更新文章内容
    if foundPost != nil {
        err := json.NewDecoder(r.Body).Decode(foundPost)
        if err != nil {
            http.Error(w, "Invalid request body", http.StatusBadRequest)
            return
        }

        // 返回更新后的文章的JSON表示
        json.NewEncoder(w).Encode(foundPost)
    } else {
        http.NotFound(w, r)
    }
}
删除资源 (Delete - DELETE)

删除资源是指向服务器发送请求以删除现有资源的过程。

示例: 删除文章:

// 处理删除文章的请求
func deletePost(w http.ResponseWriter, r *http.Request) {
    // 从请求参数中获取文章ID
    params := r.URL.Query()
    id, err := strconv.Atoi(params.Get("id"))
    if err != nil {
        http.Error(w, "Invalid ID", http.StatusBadRequest)
        return
    }

    // 查找要删除的文章
    var index int
    for i, post := range posts {
        if post.ID == id {
            index = i
            break
        }
    }

    // 如果找到文章,将其从文章列表中删除
    if index < len(posts) {
        posts = append(posts[:index], posts[index+1:]...)
        w.WriteHeader(http.StatusNoContent)
    } else {
        http.NotFound(w, r)
    }
}
解释
  1. 创建资源: 发送POST请求到/posts,并在请求体中包含新文章的JSON数据,服务器会返回创建的文章的JSON表示。

  2. 读取资源: 发送GET请求到/posts,服务器会返回包含所有文章的JSON数组。发送GET请求到/post?id=1,服务器会返回ID为1的文章的JSON表示。

  3. 更新资源: 发送PUT请求到/post?id=1,并在请求体中包含更新后的文章的JSON数据,服务器会返回更新后的文章的JSON表示。

  4. 删除资源: 发送DELETE请求到/post?id=1,服务器会删除ID为1的文章,并返回状态码204 No Content。

2.3 RESTful小建议

当设计RESTful API时,遵循一些最佳实践可以帮助确保API的可靠性、可扩展性和易用性。以下是一些RESTful API设计的最佳实践,以及使用Go语言的示例:

使用合适的HTTP方法
  • GET: 用于获取资源。
  • POST: 用于创建新资源。
  • PUT: 用于更新现有资源。
  • DELETE: 用于删除资源。
使用合适的状态码
  • 200 OK: 表示成功处理了请求。
  • 201 Created: 表示成功创建了资源。
  • 204 No Content: 表示成功处理了请求,但没有返回任何内容。
  • 400 Bad Request: 表示客户端发送了无效的请求。
  • 404 Not Found: 表示请求的资源未找到。
  • 500 Internal Server Error: 表示服务器在处理请求时发生了意外错误。
使用合适的URI
  • 使用名词来表示资源,避免使用动词。
  • 使用复数形式来表示资源的集合。
  • 使用斜杠来表示资源之间的层级关系。
使用合适的版本控制
  • 在URI中包含版本号,以便在API更新时保持向后兼容性。

示例: /v1/posts 表示版本1的文章资源。

使用合适的身份验证和授权机制
  • 使用OAuth或JWT等标准协议进行身份验证和授权。
  • 对需要授权访问的资源进行权限验证。
使用合适的错误处理机制
  • 返回清晰的错误信息,帮助客户端理解问题所在。
  • 使用统一的错误格式,方便客户端处理错误。

示例: 在处理错误时,返回包含错误信息的JSON格式数据。

使用合适的数据格式
  • 使用JSON作为数据交换格式,因为它易于阅读和解析。

示例: 在Go语言中,可以使用encoding/json包来处理JSON数据的编码和解码。

使用合适的缓存策略
  • 使用HTTP缓存头来控制缓存行为,提高性能和减少服务器负载。

示例: 使用Cache-ControlETag等HTTP头来控制缓存。

使用合适的文档和测试工具
  • 提供清晰的API文档,包括资源、URI、参数、返回结果等信息。
  • 使用自动化测试工具来测试API的功能和性能。

示例: 使用Swagger或OpenAPI规范来编写API文档,使用Postman或JUnit等工具进行API测试。

二、架构设计

具体参照我的博客:架构模式—索引目录

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风不归Alkaid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值