Я занимаюсь разработкой мобильного приложения с аутентификацией и подписками с apollo graphql на внешнем интерфейсе и Django на внутреннем. Я не могу заставить работать websocket.
Пользователь может войти (закрыть приложение и вернуться в систему), а также запускать мутации / запросы, но когдаэто делает подписки, веб-сокет, кажется, не соединяется.Кроме того, при запуске метода подписки subscribe()
, onDisconnected
listener (?) Срабатывает, однако, onConnected
или onReconnected
никогда не срабатывает, а onReconnecting
срабатывает только тогда, когда connectionParams
содержит authToken: storage.getCookie()
(что я считаю неправильным способом его настройки в любом случае).
Практически говоря, при использовании функции обмена сообщениями сообщение отправляется, но текущий пользователь не добавляется как часть контекста отправляемого сообщения итаким образом, подписка возвращает сообщение, как если бы пользователь не отправил его (Django видит AnonymousUser
, который по умолчанию не получен для пользователя контекста).
Мне кажется, что я поцарапал Интернет, и я консультируюсь какпоследнее прибежище / отчаяние.
Любой небольшой совет / подсказка поможет!
Если вам потребуется дополнительная информация, я был бы рад поделиться.
У меня есть следующая конфигурация для моих ссылок Apollo (веб-сокет и обычная):
import { ApolloLink } from 'apollo-link'
import { ApolloClient } from 'apollo-boost'
import { createUploadLink } from 'apollo-upload-client'
import { WebSocketLink } from 'apollo-link-ws'
import { SubscriptionClient } from 'subscriptions-transport-ws'
import { setContext } from 'apollo-link-context'
import { getMainDefinition } from 'apollo-utilities'
import {
InMemoryCache,
IntrospectionFragmentMatcher,
} from 'apollo-cache-inmemory'
import introspectionQueryResultData from './fragmentTypes.json'
import * as storage from '../src/storage'
import { auth } from 'react-native-firebase'
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
})
let api = 'localhost:8000'
let domainApi = `https://${api}`
let domainWs = `wss://${api}`
if (api.startsWith('localhost')) {
// In local env, use non-secure protocols
domainApi = `http://${api}`
domainWs = `ws://${api}`
}
const defaultOptions = {
watchQuery: {
fetchPolicy: 'cache-and-network',
errorPolicy: 'all',
},
query: {
fetchPolicy: 'cache-and-network',
errorPolicy: 'all',
},
mutate: {
errorPolicy: 'all',
},
}
// Persisting sessions with react-native: https://build.affinity.co/persisting-sessions-with-react-native-4c46af3bfd83
// apollo-http-link: https://www.apollographql.com/docs/link/links/http.html
const authLink = setContext(request =>
storage.getCookie().then(cookie => ({
// Before the request is sent: set cookie header
headers: {
cookie,
},
credentials: 'omit', // set cookies manually
}))
)
const setTokenLink = new ApolloLink((operation, forward) => {
// Send the request to the server
return forward(operation).map(response => {
// After response is returned: store cookie in local storage
const context = operation.getContext()
const {
response: { headers },
} = context
if (headers) {
storage.setCookie(headers.get('set-cookie'))
}
return response
})
})
const httpLink = createUploadLink({
uri: `${domainApi}/graphql/`,
})
const wsClient = new SubscriptionClient(domainWs,
storage.getCookie().then(cookie => ({
lazy: true,
reconnect: true,
connectionParams: {
// authorization: `Bearer ${cookie}`, //tried this but no avail
authorization: cookie, // cookie = 'sessionid=abc11234.....'
authToken: storage.getCookie() // (A) gets as far as trying to reconnect
}
}))
)
wsClient.onConnected(()=>{console.log("connected f client f onConnected")})
wsClient.onReconnected(()=>{console.log("connected f client freconnected")})
wsClient.onReconnecting(()=>{console.log("connected f client f reconnecting")})
wsClient.onDisconnected(()=>{console.log("connected f client f onDisconnected")})
wsClient.onError(()=>{console.log("connected f client f onError")})
const wsLink = new WebSocketLink(wsClient)
const requestLink = ({ queryOrMutationLink, subscriptionLink }) =>
ApolloLink.split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
subscriptionLink,
queryOrMutationLink
)
const cache = new InMemoryCache({ fragmentMatcher })
export { wsClient }
export default new ApolloClient({
link: requestLink({
queryOrMutationLink: authLink.concat(setTokenLink.concat(httpLink)),
subscriptionLink: wsLink,
}),
cache,
defaultOptions,
})
И это функции хранения файлов cookie:
import AsyncStorage from '@react-native-community/async-storage'
const sessionIdRe = /sessionid=[^;]*/
export const HTTP_COOKIE = 'HTTP_COOKIE'
let cookieCache
export const getCookie = handleError(() =>
cookieCache !== undefined
? Promise.resolve(cookieCache)
: AsyncStorage.getItem(HTTP_COOKIE)
)
export const setCookie = handleError(cookie => {
const match = sessionIdRe.exec(cookie)
if (!match) {
return Promise.resolve(null)
}
const sessionCookie = match[0]
cookieCache = sessionCookie
return AsyncStorage.setItem(HTTP_COOKIE, sessionCookie)
})
export const removeCookie = handleError(() =>
AsyncStorage.removeItem(HTTP_COOKIE).then(() => (cookieCache = undefined))
)
function handleError(callback) {
return async function() {
try {
const value = await callback.apply(this, arguments)
if (value !== null) return value
} catch (e) {}
}
}