【Gin框架入门到精通系列07】Gin中的参数验证

GIN框架介绍以及使用 李文周的博客 阅读详情
AI摘要

本文是Gin框架入门到精通系列的第七篇,介绍了Gin框架中的参数验证机制。阐述了参数验证的重要性、实现原理,讲解了参数绑定与验证、内置验证标签和自定义验证器。还给出代码实践、实用技巧,最后提供进阶学习资源和实践建议。

📚 原创系列: “Gin框架入门到精通系列”

🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。

🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Gin框架技术文章。

📑 Gin框架学习系列导航

本文是【Gin框架入门到精通系列】的第7篇,点击下方链接查看更多文章

👉 数据交互篇
  1. Gin连接数据库
  2. Gin中的中间件机制
  3. Gin中的参数验证👈 当前位置
  4. Gin中的Cookie和Session管理
  5. Gin中的文件上传与处理

🔍 查看完整系列文章

📖 文章导读

在本文中,您将学习到:

  • 参数验证在Web应用安全中的核心作用和最佳实践
  • Gin框架内置验证标签的全面使用指南(20+标签详解)
  • 自定义验证器开发技巧与高级验证策略
  • 多语言验证错误处理与友好提示设计
  • 复杂数据结构验证方案和性能优化建议
  • 真实业务场景下的验证实例和解决方案

Web应用程序的安全性和稳定性很大程度上依赖于对输入数据的严格验证。掌握Gin的参数验证机制,能让您的应用更加健壮,同时提供更好的用户体验。

一、导言部分

1.1 本节知识点概述

本文是Gin框架入门到精通系列的第七篇文章,主要介绍Gin框架中的参数验证机制。通过本文的学习,你将了解到:

  • 参数验证的重要性和基本概念
  • Gin框架的参数绑定与验证流程
  • 内置验证标签的使用方法
  • 自定义验证器的实现
  • 常见验证场景的处理方法

参数验证是Web开发中保证数据完整性和安全性的重要环节,掌握Gin的参数验证机制有助于构建更加健壮的应用程序。

1.2 学习目标说明

完成本节学习后,你将能够:

  • 理解Gin参数绑定与验证的工作原理
  • 熟练使用各种内置验证标签
  • 开发自定义验证器满足特定业务需求
  • 处理验证错误并返回友好的提示信息
  • 实现复杂数据结构的嵌套验证

1.3 预备知识要求

学习本教程需要以下预备知识:

  • 基本的Go语言知识
  • HTTP请求/响应基础
  • JSON/XML数据格式
  • 已完成前六篇教程的学习

💡 学习建议:本文内容丰富,建议边读边尝试编写示例代码,通过实际操作加深理解。参数验证是构建安全Web应用的关键环节,值得投入时间掌握。

二、理论讲解

2.1 参数验证的基本概念

2.1.1 为什么需要参数验证

在Web应用中,用户输入的数据往往不可信,可能存在以下问题:

问题类型 说明 示例 潜在风险
数据格式错误 数据不符合预期格式 邮箱格式不正确 系统处理异常、功能错误
数据类型不符 类型与期望不一致 需要数字却提供了字符串 类型转换错误、程序崩溃
数据范围越界 数值超出有效范围 年龄为负数或超大数值 数据库异常、业务逻辑错误
缺少必要字段 必填数据未提供 未提供用户名 数据不完整、业务处理失败
恶意数据注入 注入攻击代码 SQL注入、XSS攻击 安全漏洞、数据泄露、权限提升

⚠️ 安全警告:根据OWASP Top 10安全风险,输入验证不足是导致注入攻击、跨站脚本攻击等高危漏洞的主要原因。

参数验证可以在服务端逻辑处理前拦截这些问题,提高应用的安全性和稳定性。正确实施参数验证具有以下优势:

  • 安全防护:防止各类注入攻击和恶意输入
  • 数据完整性:确保业务数据符合预期规则
  • 提升用户体验:及早发现并提示问题,减少用户操作错误
  • 减少错误处理:降低业务逻辑中的错误处理复杂度
  • 降低系统负载:无效请求早期拦截,避免不必要的处理开销
2.1.2 Gin验证的实现原理

Gin框架的参数验证基于功能强大的go-playground/validator库,采用标签(tag)的方式定义验证规则。这种方式易于使用且与Go的结构体标签系统完美集成。

验证工作流程图:

┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
│  请求数据接收   │  →   │  绑定到结构体   │  →   │  解析验证标签   │
└────────┬────────┘      └────────┬────────┘      └────────┬────────┘
         │                        │                        │
         ↓                        ↓                        ↓
┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
│ 执行字段验证    │  ←   │  执行结构体验证  │  ←   │  类型转换检查   │
└────────┬────────┘      └────────┬────────┘      └────────┬────────┘
         │                        │                        │
         ↓                        ↓                        ↓
┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
│ 收集验证错误    │  →   │  生成错误消息   │  →   │  返回验证结果   │
└─────────────────┘      └─────────────────┘      └─────────────────┘

Gin集成的验证器支持多种验证方式:

  • 基本类型验证:检查字段是否满足基本要求,如必填、长度、取值范围等
  • 跨字段比较:验证字段之间的关系,如两个日期的先后、两个字段的相等性
  • 嵌套结构验证:支持复杂嵌套数据结构的递归验证
  • 切片和映射验证:可以验证数组、切片、映射中的每个元素
  • 自定义函数验证:支持通过自定义函数实现复杂的验证逻辑

🔍 技术细节:Gin的验证器使用反射(reflection)机制在运行时检查结构体标签和字段值,因此比手动验证更加简洁但略有性能开销。在极端高性能场景下,可能需要考虑手动优化。

2.2 参数绑定与验证

2.2.1 绑定和验证的关系

在Gin中,**绑定(Binding)和验证(Validation)**通常是一体的过程:

  • 绑定:将请求数据(JSON、XML、Form等)解析到Go结构体中
  • 验证:检查绑定后的结构体字段是否符合预定义的规则

这种一体化设计简化了API开发流程,使代码更简洁。

Gin提供了两类绑定方法:

方法类型 代表函数 请求处理 适用场景
ShouldBind类 ShouldBindJSON() 绑定失败返回错误,不中断请求 需要自定义错误处理,或需要进行多次绑定尝试
MustBind类 BindJSON() 绑定失败自动返回400错误并中断请求 简单场景,不需要自定义错误处理

💡 最佳实践:通常推荐使用ShouldBind系列函数,它们提供更灵活的错误处理方式,并允许开发者返回更友好的错误信息。

2.2.2 常见的绑定函数

Gin支持多种数据源的绑定,适应不同的请求类型:

// JSON数据绑定
c.ShouldBindJSON(&obj)  // 从请求体解析JSON
c.BindJSON(&obj)        // 同上,但失败时自动返回错误

// XML数据绑定
c.ShouldBindXML(&obj)   // 从请求体解析XML
c.BindXML(&obj)         // 同上,但失败时自动返回错误

// 表单数据绑定
c.ShouldBind(&obj)      // 根据Content-Type自动选择绑定方式
c.ShouldBindQuery(&obj) // 仅绑定查询参数
c.ShouldBindForm(&obj)  // 仅绑定表单数据
c.ShouldBindUri(&obj)   // 绑定URL路径参数

// 绑定头部信息
c.ShouldBindHeader(&obj) // 绑定HTTP头部信息

完整的使用示例:

type LoginForm struct {
   
   
    Username string `json:"username" binding:"required" example:"admin"`
    Password string `json:"password" binding:"required" example:"******"`
}

func login(c *gin.Context) {
   
   
    var form LoginForm
    // 绑定JSON数据
    if err := c.ShouldBindJSON(&form); err != nil {
   
   
        // 验证失败,返回错误信息
        c.JSON(http.StatusBadRequest, gin.H{
   
   
            "error": err.Error(),
            "code": 400,
            "message": "请求参数验证失败",
        })
        return
    }
    
    // 验证通过,处理登录逻辑...
    if form.Username == "admin" && form.Password == "password" {
   
   
        c.JSON(http.StatusOK, gin.H{
   
   
            "code": 200,
            "message": "登录成功",
            "data": gin.H{
   
   
                "token": "example-token",
                "expires_in": 3600,
            },
        })
    } else {
   
   
        c.JSON(http.StatusUnauthorized, gin.H{
   
   
            "code": 401,
            "message": "用户名或密码错误",
        })
    }
}

📘 参数绑定顺序:当使用ShouldBind()函数时,Gin会根据Content-Type头部信息选择合适的绑定器。如果请求同时包含多种数据(如URI参数、查询参数和JSON体),可能需要使用特定的绑定函数分别处理。

2.3 内置验证标签

Gin通过go-playground/validator库提供了大量内置验证标签,满足大多数常见的验证需求。以下是按类别组织的常用验证标签:

2.3.1 基本验证标签

这些标签用于验证字段的基本属性:

type User struct {
   
   
    // 必填字段
    Username string `json:"username" binding:"required" example:"johndoe"`
    
    // 字符串长度在3-20之间
    Nickname string `json:"nickname" binding:"min=3,max=20" example:"Johnny"`
    
    // 年龄在1-120之间
    Age      int    `json:"age" binding:"gte=1,lte=120" example:"30"`
    
    // 必须为有效邮箱
    Email    string `json:"email" binding:"required,email" example:"john@example.com"`
    
    // 必须为有效URL
    Website  string `json:"website" binding:"url" example:"https://johndoe.com"`
    
    // 创建时间(可选)
    Created  time.Time `json:"created_at" binding:"omitempty" example:"2023-01-01T12:00:00Z"`
}

常用基本验证标签说明:

标签 说明 示例
required 字段必须存在且非零值 binding:"required"
omitempty 如果字段为空,跳过其他验证 binding:"omitempty,email"
len=x 长度必须等于x binding:"len=11"
min=x 最小长度/值为x binding:"min=6"
max=x 最大长度/值为x binding:"max=100"
eq=x 等于x binding:"eq=10"
ne=x 不等于x binding:"ne=0"
gt=x 大于x binding:"gt=0"
gte=x 大于等于x binding:"gte=1"
lt=x 小于x binding:"lt=100"
lte=x 小于等于x binding:"lte=99"
alpha 只包含字母 binding:"alpha"
alphanum 只包含字母和数字 binding:"alphanum"
numeric 只包含数字 binding:"numeric"
email 有效的电子邮箱 binding:"email"
url 有效的URL binding:"url"
uri 有效的URI binding:"uri"
uuid 有效的UUID binding:"uuid"
uuid3 有效的UUID v3 binding:"uuid3"
uuid4 有效的UUID v4 binding:"uuid4"
uuid5 有效的UUID v5 binding:"uuid5"
ip 有效的IP地址 binding:"ip"
ipv4 有效的IPv4地址 binding:"ipv4"
ipv6 有效的IPv6地址 binding:"ipv6"
json 有效的JSON字符串 binding:"json"
2.3.2 条件验证标签

这些标签用于根据其他字段的值进行条件验证:

type RegistrationForm struct {
   
   
    // 同意条款必须为true
    AgreeTerms bool   `json:"agree_terms" binding:"required,eq=true"`
    
    // 验证码必填(当注册方式为email时)
    VerifyCode string `json:"verify_code" binding:"required_if=RegType email"`
    
    // 注册类型必须是phone或email
    RegType    string `json:"reg_type" binding:"required,oneof=phone email"`
    
    // 若RegType为phone则Phone必填,若为email则Email必填
    Phone      string `json:"phone" binding:"required_if=RegType phone,omitempty,e164"`
    Email      string `json:"email" binding:"required_if=RegType email,omitempty,email"`
}

常用条件验证标签说明:

标签 说明 示例
oneof=x y z 值必须是列举的值之一 binding:"oneof=male female other"
required_if=Field Value 如果Field等于Value,则必填 binding:"required_if=PayMethod credit"
required_unless=Field Value 除非Field等于Value,否则必填 binding:"required_unless=OptOut true"
required_with=Field 如果Field存在,则必填 binding:"required_with=Address"
required_with_all=Field1 Field2 如果所有字段都存在,则必填 binding:"required_with_all=Address Phone"
required_without=Field 如果Field不存在,则必填 binding:"required_without=Email"
required_without_all=Field1 Field2 如果所有字段都不存在,则必填 binding:"required_without_all=Email Phone"
excluded_if=Field Value 如果Field等于Value,则必须为零值 binding:"excluded_if=Type digital"
excluded_unless=Field Value 除非Field等于Value,否则必须为零值 binding:"excluded_unless=Type physical"

💡 提示:条件验证标签非常适合处理包含多种选项的表单,如多种登录方式、支付方式等。

2.3.3 跨字段验证标签

这些标签用于验证字段之间的关系:

type PasswordReset struct {
   
   
    // 原密码
    OldPassword string `json:"old_password" binding:"required"`
    
    // 新密码,不能与原密码相同
    NewPassword string `json:"new_password" binding:"required,nefield=OldPassword"`
    
    // 确认密码,必须与新密码相同
    Confirm     string `json:"confirm_password" binding:"required,eqfield=NewPassword"`
}

type DateRange struct {
   
   
    // 开始日期
    StartDate time.Time `json:"start_date" binding:"required"`
    
    // 结束日期,必须晚于开始日期
    EndDate   time.Time `json:"end_date" binding:"required,gtfield=StartDate"`
}

常用跨字段验证标签说明:

标签 说明 示例
eqfield=Field 必须等于另一个字段的值 binding:"eqfield=Password"
nefield=Field 必须不等于另一个字段的值 binding:"nefield=OldPassword"
gtfield=Field 必须大于另一个字段的值 binding:"gtfield=MinAmount"
gtefield=Field 必须大于等于另一个字段的值 binding:"gtefield=MinAmount"
ltfield=Field 必须小于另一个字段的值 binding:"ltfield=MaxAmount"
ltefield=Field 必须小于等于另一个字段的值 binding:"ltefield=MaxAmount"
2.3.4 切片和映射验证

这些标签用于验证数组、切片和映射:

type Product struct {
   
   
    // 标签列表,最少1个,最多5个
    Tags []string `json:"tags" binding:"required,min=1,max=5,dive,required"`
    
    // 价格映射,键必须是1-10之间,值必须是正数
    Prices map[int]float64 `json:"prices" binding:"required,dive,keys,gt=0,lt=11,endkeys,gt=0"`
    
    // 图片URL列表,每个都必须是有效URL
    Images []string `json:"images" binding:"omitempty,dive,url"`
}

切片和映射验证标签说明:

标签 说明 用途
dive 向下深入验证嵌套项 验证切片/数组中的元素
keys 开始验证映射的键 验证映射键
endkeys 结束验证映射的键,开始验证值 映射验证中的分隔符

🔍 深入说明:dive标签是处理嵌套结构的关键,它告诉验证器对容器内的每个元素应用后续的验证规则。

2.4 自定义验证器

2.4.1 注册自定义验证函数

当内置验证器无法满足需求时,可以注册自定义验证函数:

package main

import (
    "regexp"
    
    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/binding"
    "github.com/go-playground/validator/v10"
)

// 中国手机号验证函数
func validateChinaPhone(fl validator.FieldLevel) bool {
   
   
    phone := fl.Field().String()
    // 中国手机号正则表达式
    pattern := regexp.MustCompile(`^1[3-9]\d{9}$`)
    return pattern.MatchString(phone)
}

func main() {
   
   
    r := gin.Default()
    
    // 获取验证器
    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
   
   
        // 注册自定义验证器
        v.RegisterValidation("china_phone", validateChinaPhone)
    }
    
    r.POST("/register", register)
    r.Run(":8080")
}

type RegisterForm struct {
   
   
    Username string `json:"username" binding:"required"`
    // 使用自定义验证器
    Phone    string `json:"phone" binding:"required,china_phone"`
}

func register(c *gin.Context) {
   
   
    var form RegisterForm
    if err := c.ShouldBindJSON(&form); err != nil {
   
   
        c.JSON(400, gin.H{
   
   "error": err.Error()})
        return
    }
    c.JSON(200, gin.H{
   
   "message": "注册成功"})
}

📋 实现说明:自定义验证函数必须接收validator.FieldLevel参数并返回布尔值。通过fl.Field()可以获取要验证的字段值。

2.4.2 带参数的自定义验证器

自定义验证器也可以接收参数:

// 注册带参数的验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok 
golang——Gin框架及路由介绍 gin框架中采用的路由库是基于httprouter做的支持Restful风格的API,意思是"表面层状态转化",是一个互联网应用程序的API设计理念:URL定位资源。可以创建路由组,为了管理相同的URL。import ("net/http"//将应用切换到“发布模式”以提升性能//创建路由//默认使用了两个中间件Logger(),Recovery()})})//路由组1})})//路由组2})})//监听端口,默认绑定端口8080。 阅读详情

相关推荐

【Gin框架入门到精通系列】全系列导航手册

本文是Gin框架入门到精通系列的导航手册,系统梳理了从入门到精通的25篇学习资源。包含基础入门、中间件、高级功能、测试部署和架构实战等全方位内容,为您提供清晰的Gin框架学习路径。

GopherTribe的博客 3396

【Gin框架入门到精通系列06】Gin中的中间件机制

本文详细介绍了Gin框架中中间件的工作原理、内置中间件使用及自定义中间件开发,并通过丰富的代码实例展示中间件在请求处理、权限控制、性能优化等方面的应用,帮助开发者构建更加健壮的Go Web应用。

GopherTribe的博客 1276

【Gin框架入门到精通系列09】Gin中的文件上传与处理

本文详细介绍了Gin框架中的文件上传与处理功能,包括单文件和多文件上传实现、文件验证安全处理以及高效的文件存储策略,帮助您构建功能完善的文件处理系统。

GopherTribe的博客 1546

【Gin框架入门到精通系列01】Gin框架介绍与环境搭建

本文是Gin框架入门到精通系列的第一篇,系统介绍Gin框架的基本概念、核心特性以及环境搭建。无论你是Go语言新手还是有经验的开发者,都能通过本文快速掌握Gin框架的基础知识,创建并运行你的第一个Gin应用。

GopherTribe的博客 1560

12-Gin 中的 Session --[Gin 框架入门精讲与实战案例]

Session(会话)是Web开发中的一个重要概念,用于在一段时间内跟踪用户的状态或活动。当用户与服务器进行交互时,如登录网站、添加商品到购物车等,服务器需要一种方式来记住这些操作,以便在整个访问期间保持用户的上下文信息。这就是Session的作用所在。

桃园码工的博客 2572

Golang中Gin 自定义验证方法

在Gin框架中,可以使用binding标签实现参数的校验。但有些特殊的需求,可能需要自己定义一些校验方法。可以通过binding标签中的自定义函数来添加自定义验证逻辑。// 邮箱正则表达式if!ok {// 添加自定义验证逻辑err!= nil {return})r.Run()在上述代码中,我们通过获取Gin使用的validator引擎,然后使用方法注册email类型的验证函数。在函数中,使用正则表达式来判断邮箱格式是否正确。如果验证失败,返回验证失败的信息。。其中,参数field。

canduecho的专栏 2286

Gin入门指南:从零开始快速掌握Go Web框架Gin

中间件函数用于记录日志。路由和请求处理:Gin 提供了简单而灵活的路由功能,可以方便地定义不同URL路径的处理函数,并支持 GET、POST 等各种HTTP请求方法。函数用于返回成功结果,它接收 Gin 的上下文对象,以及三个自定义的参数,分别为状态码、提示信息和数据。一般来说,我们需要在处理请求的路由函数中使用 defer 语句将捕获的异常记录下来,所以我们可以通过中间件实现全局的异常处理。参数绑定和验证:Gin 提供了强大的参数绑定和验证功能,可以方便地将请求参数绑定到结构体中,并进行数据验证和转换。

赵士杰——成功并非一蹴而就,而是在持之以恒的努力中逐渐实现。 3085

Wio-LR1121多频段LoRa模块技术解析与应用实践

LoRa技术作为低功耗广域物联网(LPWAN)的核心通信方案,通过独特的扩频调制技术实现远距离传输与强抗干扰能力。其工作原理基于CSS(Chirp Spread Spectrum)调制,支持动态调整扩频因子(SF)和带宽(BW)来平衡传输距离与数据速率。Wio-LR1121模块作为Semtech LR1121芯片的载体,创新性地整合了sub-GHz、S-Band和2.4GHz多频段支持,配合仅1.35μA的睡眠电流,为跨境物联网部署提供了硬件基础。在智慧农业、工业监测等场景中,开发者可通过SPI接口快速集成

weixin_33742618的博客 416

ECN实战指南:如何用SAP系统搞定工程变更(附库存处理避坑技巧)

本文详细解析了如何在SAP系统中高效执行工程变更通知(ECN),特别聚焦PP模块的操作流程与库存处理技巧。通过实战案例和系统操作指南,帮助制造业企业避免库存混乱和生产中断,实现从创建到生效的全流程管理。

m5n6o7的博客 1080

用rsl_rl训练四足机器人走梅花桩:PPO算法调参实战与避坑指南

本文详细介绍了如何使用rsl_rl框架和PPO算法训练四足机器人在梅花桩上行走的实战经验。通过分析梅花桩场景的独特挑战,提供PPO算法关键参数配置策略、奖励函数设计方法以及常见问题的解决方案,帮助开发者高效实现机器人平衡控制。文章特别强调了仿真到实机的过渡技巧,最终实现98%的过桩成功率。

weixin_29103191的博客 221

一篇NS SAR ADC的学习记录

一篇NS SAR ADC的学习记录 内容包含: OverSampling Noise Shaping Dynamic Amplifier DAC with Redundancy 最近看了些有意思的SAR ADC结构,但是因为确实对模拟这个领域不太熟,导致了一系列的级联查找,最终达到了一个大概能懂的状态,为了理清思路,顺便别让看了这么些天的东西浪费了,还是计划写篇文章。 ps:由于不是一篇特别正紧的报告,参考文献就写的比较随便,且我还是个做数字的 A 13.5-ENOB, 107-μW Noise-Sha

weixin_45669724的博客 8712

手把手教你用Simulink+CANoe+Carsim搭建硬件在环仿真环境(避坑指南)

本文是一份详细的硬件在环仿真环境搭建指南,重点讲解如何将Simulink、CANoe和CarSim三大工具进行实战融合。内容涵盖软件版本兼容性、授权避坑、环境配置,并分步演示了从离线联合仿真到实现软实时同步的完整流程,旨在帮助汽车电子工程师高效构建稳定的HIL测试平台。

weixin_29092579的博客 406

【Matlab光伏功率预测】基于GRU门控循环单元的光伏功率时间序列预测(附MATLAB代码)

【Matlab光伏功率预测】基于GRU门控循环单元的光伏功率时间序列预测(附MATLAB代码)

k8291121的博客 1100

[deprecated]第四章:TTM分析: 4.2.1 存储分配ttm_bo_validate功能实现分析

ttm_bo_validate是TTM框架中负责验证和调整BufferObject物理内存分配的核心函数,主要用于显存管理。该函数通过placement参数判断BO是否需要迁移或重新分配资源,完成内存分配、迁移及缓存策略调整等操作。实现流程包括资源释放、兼容性检查、迁移保护、新资源分配、迁移实现等步骤,支持多跳迁移和临时缓冲区。典型应用于显存溢出迁移、缓存策略调整、驱动初始化等场景,确保BO始终处于预期状态。

图形/计算GPU的驱动与虚拟化技术分享园地 846

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

上一篇: 【Gin框架入门到精通系列06】Gin中的中间件机制
下一篇: 【Gin框架入门到精通系列08】Gin中的Cookie和Session管理
Gopher部落
博客等级 码龄2年 784粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

Gopher部落

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

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

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

打赏作者

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

抵扣说明:

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

余额充值