我正在尝试使用基本的Rails,Graphql,Apollo-Client设置,但在导轨端遇到422错误“无效的身份验证令牌”时出现问题。
我对apollo的使用看起来不对吗?
这是一个带有graphql gem和apollo客户端的Rails 5应用程序。
const csrfToken = document.getElementsByName('csrf-token')[0].content
const client = new ApolloClient({
networkInterface: createNetworkInterface({
uri: '/graphql',
credentials: 'same-origin',
headers: {
'X-CSRF-Token': csrfToken
}
}),
});
client.query({
query: gql`
query Camillo2 {
user {
id
email
created_at
}
}
`,
})
.then(data => console.log(data))
.catch(error => console.error(error));
Rails日志:
Started POST "/graphql" for ::1 at 2017-03-10 08:51:58 -0800
Processing by GraphqlController#create as */*
Parameters: {"query"=>"query Camillo2 {\n user {\n id\n email\n created_at\n __typename\n }\n}\n", "operationName"=>"Camillo2", "graphql"=>{"query"=>"query Camillo2 {\n user {\n id\n email\n created_at\n __typename\n }\n}\n", "operationName"=>"Camillo2"}}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms)
答案 0 :(得分:7)
自Apollo 2.0以来根据
https://github.com/apollographql/apollo-client/blob/master/Upgrade.md
就这样做
const csrfToken = document.querySelector('meta[name=csrf-token]').getAttribute('content');
const client = new ApolloClient({
link: new HttpLink({
credentials: 'same-origin',
headers: {
'X-CSRF-Token': csrfToken
}
}),
cache: new InMemoryCache()
});
答案 1 :(得分:1)
前几天我在Rails上试验过Apollo,这是适合我的配置:
var RailsNetworkInterface = apollo.createNetworkInterface('/graphql', {
credentials: 'same-origin',
headers: {
'X-CSRF-Token': $("meta[name=csrf-token]").attr("content"),
}
});
var client = new apollo.ApolloClient({networkInterface: RailsNetworkInterface})
client.query({ query: gql`{ event(id: 7) { name } }`})
但是,看起来你的工作也一样好!
您能否确认正确的令牌被发送到服务器?例如,如果您打开Chrome devtools的网络标签,然后进行查询,您是否可以在“请求标题”部分中看到X-CSRF-Token
?
此外,Rails可能正在更新DOM中的CSRF令牌,但NetworkInterface会保留旧的陈旧CSRF令牌。您能确认“请求标头”部分中的令牌是否与<meta>
标记中的当前值相匹配? (我很惊讶我自己没有遇到过这个问题,使用Turbolinks Classic。)
答案 2 :(得分:1)
为了澄清上面的一些答案,因为其中一个用户注意到将uri作为createNetworkInterface的第一个参数传递但在控制台中记录了一个弃用消息:你必须传递&#34;凭证等选项#34;和#34;标题&#34;作为选项对象的属性,如:
const networkInterface = createNetworkInterface({
uri: '/graphql',
opts: {
credentials: 'same-origin',
headers: {
'X-CSRF-TOKEN': $('meta[name=csrf-token]').attr('content'),
},
},
});
请参阅文档中的抓取下的第二个示例:http://dev.apollodata.com/core/network.html
答案 3 :(得分:1)
使用apollo-boost,它有点不同。
import ApolloClient from 'apollo-boost'
const client = new ApolloClient({
fetchOptions: {
credentials: 'same-origin',
},
request: (operation) => {
const csrfToken = document.querySelector('meta[name=csrf-token]').getAttribute('content')
operation.setContext({
headers: { "X-CSRF-Token": csrfToken }
})
},
})
答案 4 :(得分:0)
这对我有用(我使用react_on_rails):
import { ApolloClient, createNetworkInterface } from 'react-apollo';
import ReactOnRails from 'react-on-rails';
const networkInterface = createNetworkInterface({
uri: '/graphql',
opts: {
credentials: 'same-origin'
}
});
networkInterface.use([{
applyMiddleware(req, next) {
if (!req.options.headers) {
req.options.headers = {
"X-CSRF-Token": ReactOnRails.authenticityToken()
}
}
next();
}
}]);
const client = new ApolloClient({
networkInterface
});
export default client
我错过了选择:{凭据:&#39;同源&#39; } ,导致相同的错误:无法验证CSRF令牌的真实性。 完成了422个不可处理的实体
答案 5 :(得分:0)
这个对我来说很好。
const csrfToken = document.querySelector('meta[name=csrf-token]').getAttribute('content');
const link = createHttpLink({
uri: '/graphql',
credentials: 'same-origin',
headers: {
'X-CSRF-Token': csrfToken
}
});
const client = new ApolloClient({
cache: new InMemoryCache(),
link,
});
答案 6 :(得分:0)
在触发Apollo查询客户端时,我自己在/graphql
端点上遇到422错误(在我的情况下为响应)。
可能会发生2件主要事情。
1。)制作三重,四重,五重...确保您没有打错任何字。 Apollo客户端配置可能会变得非常简洁,因此请谨慎使用您编写的令牌设置代码。
// Get Token from Meta Tags on Application.html.erb
const getToken = () => document.querySelector('meta[name="csrf-token"]').getAttribute('content');
const token = getToken();
// MiddleWare Operationt that sets the CSRF token on requests to protect from forgery
const setTokenForOperation = async operation =>
operation.setContext({
headers: {
'X-CSRF-Token': token,
},
});
// Link With CSRF Token
const createLinkWithToken = () =>
new ApolloLink(
(operation, forward) =>
new Observable(observer => {
let handle;
Promise.resolve(operation)
.then(setTokenForOperation)
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
})
.catch(observer.error.bind(observer));
return () => {
if (handle) handle.unsubscribe();
};
})
);
... other apollo-link middleware stuff like error logging etc
// Tell Apollo client about the endpoint for making queries: (HTTP LINK) - this was default endpoint added by graphql install
const createHttpLink = () =>
new HttpLink({
uri: '/graphql',
credentials: 'include',
});
// and finally put it all together
export const createClient = (cache, requestLink) =>
new ApolloClient({
link: ApolloLink.from([createErrorLink(), createLinkWithToken(), createHttpLink()]),
cache,
});
2。)防止伪造会话在第三方身份验证策略(例如Devise + CanCan或w / e)上失败
如果您的阿波罗中间件模式是犹太洁食,则可能是您的身份验证策略可能阻止了该请求。
例如,我使用的是devise + cancan,这也导致了一些看似随机的422s,通过一些研究很容易解决(请参见本文https://blog.bigbinary.com/2016/04/06/rails-5-default-protect-from-forgery-prepend-false.html)
长话短说,在这一部分上,您可能需要安装应用程序控制器(尤其是从旧版本升级),并在防止伪造设置之后授权当前的用户/资源(有关参考文献的更多信息,请参阅参考资料)class ApplicationController < ActionController::Base
protect_from_forgery prepend: true, with: :exception
...
before_action .. whatever else you have going on hereafter
希望这会有所帮助。
欢呼