9. 编写第一个mutation
在本节中,您将编写第一个用于登录后端的 mutation。mutation 以登录到后端。
mutation 用于在服务器上更改数据。在此处,登录 mutation 将根据您的电子邮件地址创建一个会话。
注意:您登录到此特定服务器的方式可能与登录您自己的服务器的方式不同。登录通常由 中间件 或与 GraphQL 完全分开的一层处理,如 Oauth。此外,请记住,典型的身份验证流程通常需要密码,但在此教程中,任何人只要提供有效的电子邮件地址即可预订航班!
在Sandboxes Explorer中设计您mutation
打开 您的Sandboxes Explorer,并点击加号添加新标签。接下来,点击Schema图标返回查看您的模式,并选择 "Mutation” 来查看您的mutation列表:
向下滚动查看login
mutation:
在浏览器的右侧点击播放按钮,打开该 mutation。打开后,点击加号添加操作:
请注意红色错误提示 - 这是由于 mutation 返回的类型是 User
,这不是一个 叶 类型:你需要选择 字段 中 mutation 将返回哪个。就我们的目的而言,我们只需要 token
字段,因此通过点击它旁边的加号来添加它。
你会发现 email
也没有自动添加为一个 参数,即使它似乎没有默认值:这是因为 email
的类型是 String
- 在.GraphQL中,这意味着它不是必需的(尽管显然没有它也走不远)。
点击 email
参数旁边的加号将其添加到参数中:
你还会注意到,沙箱探索器已经在你“变量”部分添加了一个 变量来匹配登录电子邮件。
点击你的 mutation 中的 提交操作按钮。你会看到,由于你为电子邮件地址发送了 null
,你得到登录的 null
。
现在,将 null
替换为 查询变量部分的实际电子邮件地址:
按下提交操作按钮,这次你将获得实际响应:
接下来,复制操作,可以手动操作,也可以使用三点菜单的“复制操作”选项。
将变异添加到项目中
现在,你的变异起作用了,将其添加到项目中。创建一个名为Login.graphql
的文件,并将其放置在schema.graphqls
以及你的其他GraphQL文件旁边,并将变异的内容粘贴进去:
mutation Login($email: String!) {login(email: $email) {token}}
请注意:我们还将email
变量标记为非空值,通过在类型后添加!
来实现,因为我们总是希望传递它的值。
构建你的项目以生成LoginMutation
类。
将提交按钮连接到您的变异
登录后返回上一屏幕,请在Login
组合中添加一个navigateBack
lambda参数:
@Composablefun Login(navigateBack: () -> Unit) {
同样,它应该在MainActivity
中初始化:
composable(route = NavigationDestinations.LOGIN) {Login(navigateBack = {navController.popBackStack()})}
回退到Login.kt
,并创建一个执行登录变异的函数:
private suspend fun login(email: String): Boolean {val response = try {apolloClient.mutation(LoginMutation(email = email)).execute()} catch (e: ApolloException) {Log.w("Login", "Failed to login", e)return false}if (response.hasErrors()) {Log.w("Login", "Failed to login: ${response.errors?.get(0)?.message}")return false}val token = response.data?.login?.tokenif (token == null) {Log.w("Login", "Failed to login: no token returned by the backend")return false}TokenRepository.setToken(token)return true}
可能的错误情况已处理,并返回一个布尔值以指示登录是否成功。如果是,则将令牌保存在TokenRepository
中。
请注意,该函数标记为suspend
,因此需要从协程中调用。要做到这一点,请在Login
中声明一个作用域:
// Submit buttonval scope = rememberCoroutineScope()
然后,在 onClick
匿名函数中,将 TODO
替换为对 login()
的调用,并处理结果:
Button(modifier = Modifier.padding(top = 32.dp).fillMaxWidth(),onClick = {scope.launch {val ok = login(email)if (ok) navigateBack()}}) {Text(text = "Submit")}
为了改善用户体验,添加一个加载指示器,在登录进行时显示(同时禁用按钮以避免多次点击)。
// Submit buttonvar loading by remember { mutableStateOf(false) }val scope = rememberCoroutineScope()Button(modifier = Modifier.padding(top = 32.dp).fillMaxWidth(),enabled = !loading,onClick = {loading = truescope.launch {val ok = login(email)loading = falseif (ok) navigateBack()}}) {if (loading) {Loading()} else {Text(text = "Submit")}}
测试登录
前往详情页面,点击 Book,然后在登录页面输入您的电子邮件并点击 提交。现在您有一个令牌,允许您进行操作 认证。
在下一节中,您将 使用认证操作来预订航班。