Files
zhuangang/topics/backend/gin/interview.md
T

5.3 KiB
Raw Blame History

id, title, type, domain, stack, tags, level, related, week
id title type domain stack tags level related week
backend.gin.interview Gin 面试题 interview backend
gin
Gin
Web
面试
intermediate
backend.gin.routing-binding
backend.gin.middleware-auth
backend.gin.scenario.unified-api-response
1
2
3
16

Gin 面试题

在本计划里的体现:todo-api(第 1~2 周)、task-board(第 3 周)。

基础

【基础】Gin 和标准库 net/http 是什么关系?

参考答案:Gin 的 Engine 实现了 http.Handler 接口,底层仍然由 net/http 负责监听和连接管理。Gin 增加了基于前缀树的高性能路由、gin.Context(封装请求和响应、参数获取、绑定、中间件间传值)、中间件链和常用工具(JSON 渲染、校验)。所以 Gin 可以和任何标准库中间件、httptest 一起使用。

【基础】Gin 怎么获取路径参数、查询参数和请求体?

参考答案:路径参数 c.Param("id");查询参数 c.Query("k") / c.DefaultQuery("k", "默认");请求体用绑定 c.ShouldBindJSON(&req),查询参数也可以 c.ShouldBindQuery(&q) 绑定到结构体。绑定时根据结构体的 binding 标签做校验。

追问:ShouldBind 和 Bind 的区别?→ Bind* 失败时自动返回 400 并中止,ShouldBind* 只返回错误、由你决定如何响应,一般统一用后者。

【基础】中间件的执行顺序是怎样的?c.Next() 和 c.Abort() 做什么?

参考答案:按注册顺序依次执行,形成洋葱模型:c.Next() 之前的代码按顺序执行,之后的代码按相反顺序执行。c.Next() 立即执行后续的处理函数;c.Abort() 阻止后续处理函数执行,但当前函数会继续执行完,所以通常 Abort 后紧跟 return。

【基础】RESTful 接口的状态码怎么选?

参考答案:查询成功 200、创建成功 201、删除成功 204、参数错误 400、未认证 401、已认证但无权限 403、不存在 404、状态冲突(如重复创建)409、请求过多 429、服务器错误 500、下游不可用 502/503。

进阶

【进阶】JWT 和 Session 有什么区别?JWT 怎么实现退出登录?

参考答案:Session 把登录状态存在服务端(内存或 Redis),客户端只拿一个 Session ID,服务端可以随时让它失效,但需要共享存储。JWT 把用户信息签名后交给客户端,服务端只验签,无状态、易扩展,但签发后在过期前一直有效。退出登录 / 改密码的方案:短有效期访问令牌(如 15 分钟)+ 刷新令牌;服务端维护用户的令牌版本号或黑名单(存 Redis),校验时比对。

追问:令牌存 localStorage 还是 Cookie?→ localStorage 怕 XSS 窃取;httpOnly Cookie 防 XSS 但要防 CSRF(SameSite、CSRF Token)。

【进阶】为什么在 goroutine 里使用 gin.Context 要先 c.Copy()?

参考答案:Gin 用对象池复用 Context,请求处理结束后它会被重置给下一个请求使用。如果后台 goroutine 还持有原来的 c,读到的可能是别的请求的数据。c.Copy() 生成只读副本。更好的做法是只把需要的值(用户 ID、请求 ID、c.Request.Context() 派生的 context)传给后台任务。

【进阶】如何设计统一的错误处理?

参考答案:定义业务错误类型(HTTP 状态 + 业务码 + 用户提示),处理函数通过 c.Error(err) 记录错误并返回,由一个错误中间件统一转换成标准响应;未知错误和 panic 统一返回通用提示,详细信息带请求 ID 写日志。详见本技术栈的场景文档。

【进阶】Gin 的路由为什么快?

参考答案:Gin 使用 httprouter 风格的前缀树(radix tree),每个 HTTP 方法一棵树,按路径公共前缀压缩存储,匹配时间与路径长度相关,基本不受路由数量影响;同时 Context 用对象池减少内存分配。

场景题

【场景题】接口被恶意刷,怎么在 Gin 里做限流?

参考答案:单实例用令牌桶(golang.org/x/time/rate),按用户 ID 或 IP 维护一个限流器 map(注意定期清理);多实例部署需要全局限流,用 Redis(固定窗口 INCR+EXPIRE 或滑动窗口 ZSet、Lua 脚本保证原子)。超限返回 429 并带 Retry-After。更外层可以在 Nginx 或网关做 IP 级限流,应用层做用户级、接口级限流。本计划第 3 周项目一实现过。

【场景题】一个上传接口偶尔导致服务内存暴涨,怎么处理?

参考答案:限制请求体大小(http.MaxBytesReader 或 Gin 的 MaxMultipartMemory,Nginx 的 client_max_body_size);大文件流式处理而不是一次读入内存;校验文件类型和大小;上传改成前端直传对象存储(预签名 URL),后端只接收回调。

【场景题】怎么给 Gin 服务写接口测试?

参考答案:把路由组装放在一个函数里(如 New(deps) *gin.Engine),测试时注入内存版依赖;用 httptest.NewRecorder() 和 r.ServeHTTP(w, req) 直接调用,不启动端口;用表格驱动覆盖成功、参数错误、未登录、无权限、不存在、内部错误等情况;数据库相关的用集成测试单独跑。示例见 examples/unified-api。