10. 认证您的操作
在本节中,您将预订一次航班 🚀!预订航班需要向服务器进行认证,以便正确的人被送往太空!为此,因为Apollo Kotlin正在使用OkHttp 用于处理HTTP请求,你将使用一个OkHttp 拦截器 向你的 GraphQL 请求中添加头部。
添加拦截器
在Apollo.kt
中,添加 AuthorizationInterceptor
类:
app/src/main/kotlin/com/example/rocketreserver/Apollo.kt
private class AuthorizationInterceptor() : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request().newBuilder().apply {TokenRepository.getToken()?.let { token ->addHeader("Authorization", token)}}.build()return chain.proceed(request)}}
此拦截器会向请求中添加一个 "Authorization: $token"
HTTP头部,如果token非空。
使用拦截器
创建一个自定义 OkHttpClient
,它将使用此拦截器并将其传递给 ApolloClient
:
app/src/main/kotlin/com/example/rocketreserver/Apollo.kt
val apolloClient = ApolloClient.Builder().serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/graphql").okHttpClient(OkHttpClient.Builder().addInterceptor(AuthorizationInterceptor()).build()).build()
添加“预订行程”和“取消行程”的变化
在 schema.graphqls
旁边添加一个 BookTrip.graphql
文件:
app/src/main/graphql/BookTrip.graphql
mutation BookTrip($id:ID!) {bookTrips(launchIds: [$id]) {successmessage}}
注意,bookTrips
字段参数接受一个列表变量,但此突变本身只接受一个变量。
还要添加CancelTrip.graphql
文件。此突变不使用列表:
app/src/main/graphql/CancelTrip.graphql
mutation CancelTrip($id:ID!) {cancelTrip(launchId: $id) {successmessage}}
将突变连接到您的UI
返回LaunchDetails.kt
,并将TODO
替换为onBookButtonClick
执行适当的突变,根据发射启动是否预定:
app/src/main/java/com/example/rocketreserver/LaunchDetails.kt
private suspend fun onBookButtonClick(launchId: String, isBooked: Boolean, navigateToLogin: () -> Unit): Boolean {if (TokenRepository.getToken() == null) {navigateToLogin()return false}val mutation = if (isBooked) {CancelTripMutation(id = launchId)} else {BookTripMutation(id = launchId)}val response = apolloClient.mutation(mutation).execute()return when {response.exception != null -> {Log.w("LaunchDetails", "Failed to book/cancel trip", response.exception)false}response.hasErrors() -> {Log.w("LaunchDetails", "Failed to book/cancel trip: ${response.errors?.get(0)?.message}")false}else -> true}}
现在回到LaunchDetails
函数,声明一个协程作用域,以便调用onBookButtonClick
。同时,让我们记住isBooked
并相应地更改按钮的文本:
app/src/main/java/com/example/rocketreserver/LaunchDetails.kt
// Book buttonval scope = rememberCoroutineScope()var isBooked by remember { mutableStateOf(data.launch?.isBooked == true) }Button(modifier = Modifier.padding(top = 32.dp).fillMaxWidth(),onClick = {scope.launch {val ok = onBookButtonClick(launchId = data.launch?.id ?: "",isBooked = isBooked,navigateToLogin = navigateToLogin)if (ok) {isBooked = !isBooked}}}) {Text(text = if (!isBooked) "Book now" else "Cancel booking")}
我们还添加了一个加载指示器,并且在突变运行时防止按钮被点击:
app/src/main/java/com/example/rocketreserver/LaunchDetails.kt
// Book buttonvar loading by remember { mutableStateOf(false) }val scope = rememberCoroutineScope()var isBooked by remember { mutableStateOf(data.launch?.isBooked == true) }Button(modifier = Modifier.padding(top = 32.dp).fillMaxWidth(),enabled = !loading,onClick = {loading = truescope.launch {val ok = onBookButtonClick(launchId = data.launch?.id ?: "",isBooked = isBooked,navigateToLogin = navigateToLogin)if (ok) {isBooked = !isBooked}loading = false}}) {if (loading) {SmallLoading()} else {Text(text = if (!isBooked) "Book now" else "Cancel booking")}}
预订您的旅行!
编译并运行您的应用程序。您现在可以预订和取消旅行!按钮将根据旅行是否已预订而更改。
在下一节中,您将编写您的第一个订阅并在有人预订航班时实时收到通知。