9. 编写你的第一个变异操作
在本节中,你将编写第一个变异操作以登录后端。
变异操作用于更改服务器上的数据。在这里,登录变异操作将根据您的电子邮件地址创建一个会话。
注意:您登录此特定服务器的方式可能不同于您登录自建服务器的方式。登录通常是由中间件或与GraphQL完全分离的层来处理的,例如OAuth。另外请注意,一个典型的身份验证流程可能需要密码,但在这个教程中,任何人都可以使用有效的电子邮件地址预订航班!
在Sandbox探查器中原型化您的变异操作
打开 您的沙盒资源管理器,然后点击加号添加新选项卡。接下来,点击模式图标返回查看您的模式,并选择 ""突变"" 以查看您的突变字段列表:
向下滚动查看登录
字段:
点击右侧的播放按钮在资源管理器标签页中打开该 字段。打开后,点击加号将该字段添加到您的 操作:
注意红色错误指示 - 这是因为该字段的返回类型是 字段,这并不是一个 叶类型:您需要选择该突变将返回的用户哪些 字段。为了我们的目的,我们只需要 token
字段,所以通过点击它旁边的加号来添加它。
您还会注意到,虽然 email
好像没有默认值,但并没有自动将其作为 参数添加。这是因为 email
的类型是 String
- 请记住,在 GraphQL 中,这意味着它 不是必需的(尽管很明显,没有它您不会走得太远)。
点击 email
参数旁边的加号以将该参数添加:
您还会注意到,沙盒资源管理器已将一个 变量添加到“变量”部分以匹配登录电子邮件地址。
点击“提交 ”操作”按钮以执行您的 突变。您会看到,由于您为电子邮件地址发送 null
,所以登录返回 null
:
现在,将 null
从 查询 变量”部分替换为实际的电子邮件地址:
按提交操作按钮,这次你会得到实际响应:
接下来,复制操作,无论是手动操作还是使用省略号菜单中的“复制操作”选项。
将突变添加到项目
现在您的突变已工作,将其添加到您的项目。创建一个命名为Login.graphql
的文件,放在schema.graphqls
以及其他GraphQL文件旁边,并粘贴突变的内容:
mutation Login($email: String!) {login(email: $email) {token}}
注意:我们还将email
变量标识为非空,通过在类型末尾添加!
实现,因为我们始终希望传递对其的值。
构建项目生成 LoginMutation
类。
将提交按钮连接到你的突变
在登录后导航回上一屏幕,将一个 navigateBack
锋值参数添加到 Login
可组合组件:
@Composablefun Login(navigateBack: () -> Unit) {
再次,它应该在 MainActivity
中初始化:
composable(route = NavigationDestinations.LOGIN) {Login(navigateBack = {navController.popBackStack()})}
返回到 Login.kt
并创建一个执行登录突变的函数:
private suspend fun login(email: String): Boolean {val response = apolloClient.mutation(LoginMutation(email = email)).execute()return when {response.exception != null -> {Log.w("Login", "Failed to login", response.exception)false}response.hasErrors() -> {Log.w("Login", "Failed to login: ${response.errors?.get(0)?.message}")false}response.data?.login?.token == null -> {Log.w("Login", "Failed to login: no token returned by the backend")false}else -> {TokenRepository.setToken(response.data!!.login!!.token!!)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")}}
测试登录
转到详情页面,点击 预订,在登录页面,输入您的电子邮件并点击 提交。现在您有一个令牌,允许您进行 操作。
在下一节中,您将 使用受信任的操作来预订航班。