新闻详情

新闻详情

首页 / 资讯中心 / 详情

GraphQL Go 后端教程:在 Resolver 中获取登录用户并完善 CreateLink 与 Links 数据关联

发布时间:2026/9/25 4:24:17来源:尧图网络
GraphQL Go 后端教程:在 Resolver 中获取登录用户并完善 CreateLink 与 Links 数据关联
【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载本篇技术指南承接 Hackernews GraphQL API 克隆项目的认证体系构建讲解如何在 gqlgen 的 Resolver 中通过context获取登录用户对象从而补齐此前因无法鉴权而搁置的CreateLinkmutation 实现——包括写入用户外键、用 SQLINNER JOIN在查询时回填创建者信息以及最终通过 GraphQL Playground 的 HTTP Headers 携带 JWT 完成端到端验证。读完本文你将掌握中间件解析用户 → context 传递 → Resolver 读取 → 数据库外键落库 → JOIN 联查回填的完整闭环这也是大多数真实业务 API 中记录资源归属人的标准实现模式。为什么 CreateLink 之前是未完成状态在教程的早期阶段创建与检索链接章节CreateLinkmutation 的初始实现是这样写的func (r *mutationResolver) CreateLink(ctx context.Context, input model.NewLink) (*model.Link, error) { var link links.Link link.Title input.Title link.Address input.Address linkID : link.Save() return model.Link{ID: strconv.FormatInt(linkID, 10), Title: link.Title, Address: link.Address}, nil }问题很明显这条记录没有关联任何用户。虽然 Schema 中Link类型已经声明了必填的user: User!字段见入门与 Schema 定义章节但当时的实现既没有从请求中识别出是谁在创建链接也没有把用户信息写入数据库。返回的model.Link中User字段为空本质上是在伪造数据。现在认证体系已经就绪认证实现章节 完成了 JWT 生成/解析与认证中间件认证端点章节 完成了注册、登录与刷新 Token 三个 mutation我们就可以回头把这块补完。认证中间件如何把用户塞进 context要理解 Resolver 中的取用户逻辑先回顾认证中间件internal/auth/middleware.go的两个关键函数var userCtxKey contextKey{user} // Middleware 在每个请求进入 Resolver 前执行 func Middleware() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { header : r.Header.Get(Authorization) // 未携带 header 时直接放行匿名请求 if header { next.ServeHTTP(w, r) return } // 解析 JWT得到用户名 username, err : jwt.ParseToken(header) if err ! nil { http.Error(w, Invalid token, http.StatusForbidden) return } // 根据用户名查库得到用户 ID id, err : users.GetUserIdByUsername(username) // 把用户对象放入 context ctx : context.WithValue(r.Context(), userCtxKey, user) r r.WithContext(ctx) next.ServeHTTP(w, r) }) } } // ForContext 从 context 中取出用户前提是 Middleware 已经执行过 func ForContext(ctx context.Context) *users.User { raw, _ : ctx.Value(userCtxKey).(*users.User) return raw }整个链路是请求带Authorization头 → 中间件解析出 JWT 中的用户名 → 查库拿到用户 ID → 构造*users.User塞进context→ Resolver 通过auth.ForContext(ctx)取回。需要特别注意的是中间件对匿名请求是放行的header 时直接next.ServeHTTP所以ForContext返回的可能是nil——这正是下文access denied分支的判定依据。补全 CreateLink从 context 读取登录用户现在回到schema.resolvers.go为CreateLink加上鉴权与归属逻辑func (r *mutationResolver) CreateLink(ctx context.Context, input model.NewLink) (*model.Link, error) { // 1 user : auth.ForContext(ctx) if user nil { return model.Link{}, fmt.Errorf(access denied) } . . . // 2 link.User user linkID : link.Save() graphqlUser : model.User{ ID: user.ID, Name: user.Username, } return model.Link{ID: strconv.FormatInt(linkID, 10), Title: link.Title, Address: link.Address, User: graphqlUser}, nil }代码只改了两处但含义关键1鉴权守卫auth.ForContext(ctx)从 context 取出用户对象如果取到nil说明请求没带合法 JWT、中间件放行了匿名请求立即返回空 Link 与access denied错误阻止未登录用户写入数据。这是所有需要登录才能操作的 mutation 的通用入口写法。2写入归属把当前登录用户赋给link.User后调用link.Save()让数据库层能拿到user.ID写入外键返回时构造 GraphQL 层的model.User注意这里把ID与Username映射为 GraphQL 的id与name字段。这里体现了教程此前提到过的双结构体设计links.Link数据库层含User *users.User与model.LinkGraphQL 层含User *model.User各司其职Resolver 负责两者之间的转换。完善 Links 查询返回每条链接的创建者在补全写入侧之后查询侧也要同步升级——Links查询此前只返回ID/Title/Address现在要把每条链接的创建者一并返回func (r *queryResolver) Links(ctx context.Context) ([]*model.Link, error) { var resultLinks []*model.Link var dbLinks []links.Link dbLinks links.GetAll() for _, link : range dbLinks { graphqlUser : model.User{ ID: link.User.ID, Name: link.User.Username, } resultLinks append(resultLinks, model.Link{ID: link.ID, Title: link.Title, Address: link.Address, User: graphqlUser}) } return resultLinks, nil }改动点集中在循环体内直接从dbLinks里取出的每条links.Link读取其User字段转换成model.User后装配进结果。前提是数据库层的GetAll()必须真的把User字段填充好——这正是下一节要解决的 JOIN 问题。数据库层改动一Save 方法写入用户外键internal/links/links.go的Save方法原来只插入标题和地址stmt, err : database.Db.Prepare(INSERT INTO Links(Title,Address) VALUES(?,?)) res, err : stmt.Exec(link.Title, link.Address)现在要插入用户外键改为三列stmt, err : database.Db.Prepare(INSERT INTO Links(Title,Address, UserID) VALUES(?,?, ?))执行语句时把link.User.ID作为第三个参数传入res, err : stmt.Exec(link.Title, link.Address, link.User.ID)这条 SQL 之所以能成立是因为数据库迁移阶段创建的Links表本身就定义了外键约束。回顾数据库设置章节中的迁移文件000002_create_links_table.up.sqlCREATE TABLE IF NOT EXISTS Links( ID INT NOT NULL UNIQUE AUTO_INCREMENT, Title VARCHAR (255) , Address VARCHAR (255) , UserID INT , FOREIGN KEY (UserID) REFERENCES Users(ID) , PRIMARY KEY (ID) )UserID列通过FOREIGN KEY (UserID) REFERENCES Users(ID)关联到Users表主键从表结构层面保证了每条链接必然归属一个已存在的用户外键约束在插入时就会校验引用的合法性。数据库层改动二GetAll 用 INNER JOIN 回填创建者查询侧同样需要联动修改。GetAll原来的 SQL 只查Links表自身字段现在必须联查Users表才能拿到创建者的用户名。使用INNER JOIN让两条记录按UserID Users.ID配对func GetAll() []Link { stmt, err : database.Db.Prepare(select L.id, L.title, L.address, L.UserID, U.Username from Links L inner join Users U on L.UserID U.ID) // changed if err ! nil { log.Fatal(err) } defer stmt.Close() rows, err : stmt.Query() if err ! nil { log.Fatal(err) } defer rows.Close() var links []Link var username string var id string for rows.Next() { var link Link err : rows.Scan(link.ID, link.Title, link.Address, id, username) // changed if err ! nil { log.Fatal(err) } link.User users.User{ ID: id, Username: username, } // changed links append(links, link) } if err rows.Err(); err ! nil { log.Fatal(err) } return links }逐项拆解这条 JOIN 查询from Links L inner join Users U on L.UserID U.ID给Links起别名L、Users起别名U以L.UserID U.ID为连接条件。INNER JOIN只返回两边都匹配上的行——如果某条 Link 的UserID在Users表中找不到对应记录该行会被过滤掉这与外键约束的语义是一致的。select L.id, L.title, L.address, L.UserID, U.Username同时取链接自身字段与用户的Username一次查询拿到组装完整Link所需的全部数据。rows.Scan顺序必须与 select 列表严格一致依次填入link.ID、link.Title、link.Address、以及临时变量idUserID与username。随后手动构造users.User并赋值给link.User完成链接带创建者的组装。INNER JOIN是 SQL 中最基础的连接方式之一如果对它不熟悉可以查阅 SQL 教程中关于sql_join_inner的内容理解其取交集语义。端到端验证从 access denied 到成功创建所有代码改完后整个应用就闭环了。启动服务器默认端口 8080打开 GraphQL Playground先不带任何鉴权信息提交创建链接的 mutationmutation { createLink(input: {title: real link!, address: www.graphql.org}){ user{ name } } }此时因为请求头里没有Authorization中间件放行匿名请求、ForContext(ctx)返回nil于是得到{ errors: [ { message: access denied, path: [ createLink ] } ], data: null }这正是预期行为未登录用户无法提交链接access denied从源头拦截了越权写入。注意错误响应中data为null因为 Resolver 返回了非 nil 的 error。要成功创建需要在 Playground 底部点击HTTP Headers按钮填入 Authorization 头——值为登录/注册接口返回的 JWT 令牌在认证端点章节中通过createUser或loginmutation 获取{ Authorization: // use your own generated token }带上合法 token 重新提交同样的 mutation中间件会解析出用户名、查出用户 ID 放入 contextCreateLink校验通过后带着UserID落库查询时再通过 JOIN 把创建者带回来。再次执行links查询就能看到每条链接都带有创建者的name字段。至此认证与数据归属这条主线全部打通注册/登录产出 JWT → 中间件解析并注入 context → mutation 校验登录态并写入外键 → 查询用 JOIN 回填创建者。这也是后续实现只看某用户的链接删除/编辑权限校验等功能的基础设施。关键点小结鉴权入口统一auth.ForContext(ctx)配合if user nil判空是所有需要登录的 mutation 的标准守卫写法匿名请求被中间件放行所以判空是必须的。双结构体转换links.Link含*users.User与model.Link含*model.User在 Resolver 中完成互转数据库层与 GraphQL 层解耦。外键写入Save方法在INSERT语句中带上UserID配合迁移文件中的FOREIGN KEY约束保证引用完整性。JOIN 联查回填GetAll用INNER JOIN一次取回链接与创建者rows.Scan的字段顺序必须与 select 列表严格对应。端到端验证不带头部得到access denied带上 JWT 即可创建并查询到带创建者的链接验证了完整链路。赞分享【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载相关推荐How to GraphQLGo 后端用 gqlgen 实现 CreateLink 变更与 links 查询的数据库读写实战How to GraphQLGo 后端用 gqlgen 实现 CreateLink 变更与 links 查询的数据库读写实战 本篇技术指南以 How toMultiJSON与Rails集成打造高效JSON API的最佳实践MultiJSON与Rails集成打造高效JSON API的最佳实践 MultiJSON是一个为JSON处理提供通用可交换后端的Ruby库它允许开发者在不同终极VSCode Fortran插件指南5分钟快速上手教程终极VSCode Fortran插件指南5分钟快速上手教程 想要在Visual Studio Code中高效编写Fortran代码吗fortran lang网页爬虫上一篇tt-rss-feedly-theme夜间模式一键切换深色主题与toggle_night_mode插件详解下一篇终极PyTorch资源库从入门到专家的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Amazon Polly 与 AWS SDK for JavaScript (v3) 实战指南:文本转语音与浏览器播放 2026/9/25 5:29:10

Amazon Polly 与 AWS SDK for JavaScript (v3) 实战指南:文本转语音与浏览器播放

示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地…

阅读更多 →
Eclipse MAT 1.6.1 Windows x64 堆转储深度分析指南 2026/9/25 5:29:03

Eclipse MAT 1.6.1 Windows x64 堆转储深度分析指南

简介:本资源为Eclipse基金会开源的Java内存分析工具Memory Analyzer(MAT)1.6.1正式版Windows客户端,专为Java开发者、性能调优工程师及JVM故障排查人员设计,用于精准识别堆内存泄漏、定位大对象引用链、优化应用内存占…

阅读更多 →
Atlas 300V 24G上部署YOLOv5:从模型转换到推理优化全指南 2026/9/25 5:29:03

Atlas 300V 24G上部署YOLOv5:从模型转换到推理优化全指南

先放个结论:Atlas 300V 24G 确实是一块运算加速卡,而且是专门干推理活的加速卡,不是用来训练大模型的。最近不少做视觉项目的朋友都在问我,手里的YOLO模型到底能不能搬到这块卡上,跑起来到底比GPU差多少。这篇东西我打…

阅读更多 →
ESP32跑WebAssembly:运行时翻译,CPU无需原生支持 2026/9/25 5:28:56

ESP32跑WebAssembly:运行时翻译,CPU无需原生支持

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
【电路测试】如何测试电源纹波 2026/9/25 5:28:49

【电路测试】如何测试电源纹波

工具:1. 示波器2. 差分探头3. 板卡4. 开关电源操作步骤:1.1 示波器设置 20MHz 带宽限制,打到 交流耦合,在 Acquire中设置 Peak Detect。测量纹波要取最短回流路 径,取下探针帽和地线夹,使用接 地环&#xf…

阅读更多 →
pysc2 地图系统详解:Map 配置类、Mini-Game、Ladder 与 Melee 三类地图的定义与使用 2026/9/25 5:28:43

pysc2 地图系统详解:Map 配置类、Mini-Game、Ladder 与 Melee 三类地图的定义与使用

人工智能强化学习 【免费下载链接】pysc2 StarCraft II Learning Environment 项目地址: https://gitcode.com/gh_mirrors/py/pysc2 点击查看 免费下载 本文基于 pysc2 仓库中的 maps 文档 及配套源码,讲解 SC2 地图在 pysc2 中的组织方式:S…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉