作为主题。 谢谢
const errorLink = onError(({ networkError }) => {
if (networkError.status === 401) {
logout();
}
})
据我所知,如果没有响应(例如服务器关闭),就会发生“无法获取”。 那是因为您不会看到任何状态代码。 如果我错了,请纠正我。
但我认为在这种情况下,只拥有一条短信并不是最好的主意——应该有一些更容易处理的东西(比如错误代码)。
我确实在 chrome Inspector 的网络标签中看到请求正在发送和输入 (401)。
我认为这与#218有关(不确定..)
也有这个问题。 networkError.status
或networkError.statusCode
丢失且响应对象丢失。 无法在网络层处理 HTTP 状态代码。
我目前也有这个问题。 它应该在最新版本中得到修复,但我仍然遇到这个问题。
对此@jbaxleyiii有何想法?
我很遗憾听到您@akrigline和@bogdansoare仍然遇到此问题! 这绝对应该被修复。
我现在正在处理 #364。 同时,您能否创建或修改此代码沙盘以重现错误? 或者甚至更好地打开一个测试失败的 PR?
任何新闻?
同样有这个问题,响应对象是空的,并且 networkError 对象上没有 statusCode。
我什至没有得到networkError
属性:
编辑:很可能#475
我已经通过忽略Error
类型解决了这个问题,但这是一个完全临时的解决方案,我们必须等待未来的修复。 @埃文斯
我们写的关于它们的networkError
的当前不是真正的服务器端错误。 总之,在这些情况下,我们根本不会有statusCode
,事实上,我们需要在 graphql 服务器上创建或接收真正的网络错误。 (_如果您只是与服务器的通信问题,则无法访问statusCode
_)。
如果您使用的是 TypeScript,您可以看到networkError
具有以下类型:
export interface ErrorResponse {
graphQLErrors?: GraphQLError[];
networkError?: Error;
response?: ExecutionResult;
operation: Operation;
}
而Error
定义为:
interface Error {
name: string;
message: string;
stack?: string;
}
我们可以看到这里没有定义statusCode
,所以我们不能在 TypeScript 中调用networkError.statusCode
类的东西。
我们必须将 networkError 转换为any
并且我们将其设置为空的object
。
const afterWareLink = onError(({operation, response, graphQLErrors = {}, networkError = {} as any}) => {
const status: number = networkError && networkError.statusCode ? networkError.statusCode : null;
debugHelper.error('apolloError', {
operation,
response,
graphQLErrors,
networkError,
status,
});
// Do your job
// if (status && HTTPExceptions[status])
// redirectService.redirectWithReload(`/error/${status}`);
});
根据上面的afterWareLink
代码,我们遇到了带有 400 状态码的服务器错误,现在可以访问此状态码:
这是我的 ApolloClient 定义:
import {ApolloLink, from} from 'apollo-link';
import {application} from '../../constants/application';
import {ApolloClient} from 'apollo-client';
import {InMemoryCache} from 'apollo-cache-inmemory';
import {HttpLink} from 'apollo-link-http';
import {tokenStorage} from '../middleware';
import {debugHelper} from '../helpers/debugHelper';
import {onError} from 'apollo-link-error';
import {apolloCache} from './apolloCache';
import fetch from 'unfetch';
const API = new HttpLink({
uri: application.API.URL,
fetch: fetch
});
const cache = new InMemoryCache({
dataIdFromObject: (object: any) => apolloCache.getID(object)
});
const afterWareLink = onError(({operation, response, graphQLErrors = {}, networkError = {} as any}) => {
const status: number = networkError && networkError.statusCode ? networkError.statusCode : null;
debugHelper.error('apolloError', {
operation,
response,
graphQLErrors,
networkError,
status,
});
// Do your job
// if (status && HTTPExceptions[status])
// redirectService.redirectWithReload(`/error/${status}`);
});
const middleWareLink = new ApolloLink((operation, forward) => {
const token = tokenStorage.get();
operation.setContext(context => ({
...context,
headers: {
...context.headers,
token: token ? token.token : '',
},
}));
return forward(operation);
});
export const apolloClient = new ApolloClient({
link: from([
middleWareLink,
afterWareLink,
API
]),
cache: cache,
});
希望有用。
如果您使用的是节点服务器并且可以从上下文访问您的响应对象,您可以通过添加来解决这个问题; ctx.res.status(401);
在返回您的回复之前。
现在 apollo-link-errors 和 apollo-link-retry 都能够从 networkErrors 对象中获取错误。
这是我使用自定义架构指令来确保用户通过身份验证的示例;
import { SchemaDirectiveVisitor } from "graphql-tools";
import { defaultFieldResolver } from "graphql";
import { createError } from "apollo-errors";
const AuthError = createError("AuthError", {
message: "Not authorized"
});
export class IsAuthenticatedDirective extends SchemaDirectiveVisitor {
visitObject(type) {
this.ensureFieldsWrapped(type);
}
visitFieldDefinition(field, details) {
this.ensureFieldsWrapped(details.objectType);
}
ensureFieldsWrapped(objectType) {
if (objectType._authFieldsWrapped) return;
objectType._authFieldsWrapped = true;
const fields = objectType.getFields();
Object.keys(fields).forEach(fieldName => {
const field = fields[fieldName];
const { resolve = defaultFieldResolver } = field;
field.resolve = async function(...args) {
const ctx = args[2];
const result = await resolve.apply(this, args);
if (ctx.req.user) {
return result;
} else {
ctx.res.status(401);
return null;
}
};
});
}
}
修好还是不修好? 我仍然无法访问 networkError 对象中的任何状态代码。
@artsiompeshko
我可以在 TypeScript 中以这种方式访问它:
const networkErrorLink = onError(({networkError, operation, forward}: ErrorResponse) => {
if (networkError) {
switch (networkError['statusCode']) {
case 401:
case 422:
if (window.location.pathname !== '/login') {
Logger.error('Unauthorized or stale Authorization Session. Reloading page...')
window.history.go()
}
break
}
}
return forward(operation)
})
有这方面的消息吗? 同样的问题。 我的 javascript 代码中没有可用的networkError.statusCode
。
@goldenbearkin也许我们正确使用它是错误的?
错误中应该允许网络状态码,目前没有办法访问网络状态。 networkError
实际上是一个HttpErrorResponse
类型的对象,它有一个status
属性,它总是0
,其中控制台中的原始状态代码是 401 或 403
@lvlohammadi
也可以对 graphQLErrors 执行相同的解决方法吗? 我可以使用您的解决方法查看 networkErrors 的状态代码 - 使用 504 对此进行了测试。但是当身份验证失败时,我预计 graphQLError 状态代码为 401。但目前无法访问它。 我尝试了 'graphQLErrors = {} as any' 选项,但无济于事。
这很奇怪,但实际上我在networkError
上有statusCode
属性。 但是接口中没有这样的属性,所以TS在这里输出错误。
const errorLink = onError(({ networkError, graphQLErrors, response }: ErrorResponse) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }: GraphQLError) => {
showError(message);
});
}
// @ts-ignore
console.log('statusCode', networkError.statusCode); // outputs 401
if (networkError) {
showError(networkError.message);
}
});
在服务器上我使用koa
和koa-passport
,这里对应的一段代码是:
app.use(mount(graphQlPath, passport.authenticate('jwt', { session: false })));
由于这个问题真的已经过时了,我将关闭它,但如果您仍然对此感到担忧,请随时重新打开,我会尽快回复您。
@JoviDeCroock现在面临这个问题......那么,解决方案是什么? 如何从 networkError 获取错误状态?
好吧,所以你确定这是一个 networkError(没有 graphqlError?)并且 statusCode/status 是未定义的?
这是打字稿错误还是真的未定义? 你能重现它吗?
顺便说一句,对我的评论投反对票无助于解决此错误,从来没有任何方法可以重现此问题,而且需要修剪问题以改进我们提供帮助的方式。
我有同样的问题,但这可能是因为我从服务器发送状态的方式。 我基本上是在尝试简化任何特定于 graphQL 的处理并简单地响应,例如:
res.status(404).send("The resource you are looking for cannot be found")
但是我只得到TypeError: Failed To Fetch
。 这是因为 Apollo-Server-Express 中的实现细节吗?
@JoviDeCroock实际上,在尝试为 401 或 403 实现客户端错误处理时,我在使用 graphql 代码时搞砸了一点。我们使用中间件,而我所做的只是为每个请求始终返回 401。 这样也 OPTION 请求得到 401,不应该发生什么,可能正因为如此,我根本无法看到状态。 更新代码后,我可以看到状态代码
感谢更新@dimitriy-k 很高兴听到此问题已解决。
对于@AlbertoPL ,我今晚会调查你的问题。 如果可以,请随时对此发表更多评论。
@JoviDeCroock感谢您的观看。 所以我试图开始工作的具体代码如下:
res.status(426).send()
在 Chrome 的网络选项卡中,我确实看到 /graphql 端点正在响应 426 状态代码和适当的响应(需要升级)。 但是,当使用来自apollo-link-error
的 onError 链接时,我仍然只看到TypeError: Failed to Fetch
apollo-link-error
。 如果我能得到真正的状态代码那就太好了(甚至消息就足够了)。
好吧,这是一些非常棒的信息。 我们应该能够以某种方式调试这个,你觉得可以吗?
是的,我当然可以尝试。 我很感激任何帮助/提示。
因此,只需在 Chrome 调试器中逐步执行代码,我就可以看到apollo-link-batch-http
和apollo-link-http
都使用 fetcher 函数进行 API 调用(作为链接选项或某些默认值传递,我都尝试了):
fetcher(chosenURI, options)
.then(function (response) {
operations.forEach(function (operation) { return operation.setContext({ response: response }); });
return response;
}).then(parseAndCheckHttpResponse(operations))
它永远不会进入任何 then 块,而是进入 catch 块。 到那时,错误已经设置为“无法获取”。 不存在状态代码。
我们应该在这里检查是否出错:
这个周末我会尝试制作一个复制品,因为工作中的自动取款机日程安排非常繁忙。
编辑:
主要问题是每次我测试时我都使用apollo-server
并且它运行良好,但可能是绑定或其他东西有点不同。 所以我很难在没有服务器的情况下测试这个作为复制
如果有帮助,这里是我们的 Apollo-server(注意我们使用 Apollo-server-express)。 CreateGraphQLSchema 返回我们使用的 Apollo Server 实例:
const { ApolloServer, gql } = require("apollo-server-express");
const collectFragments = container => {
return container
.$list()
.map(component => container[component])
.join("\n");
};
const mergeObjects = container => {
const types = container.$list().map(k => container[k]);
return _.merge(...types);
};
function CreateGraphQLSchema(
schema,
queries,
mutations,
subscriptions,
resolvers,
graphqlFormatError,
) {
const graphQlSchema = gql`
${collectFragments(schema.enum)}
${collectFragments(schema.type)}
type Query {
${schema.Query}
}
${collectFragments(schema.input)}
type Mutation {
${schema.Mutation}
}
type Subscription {
${schema.Subscription}
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
`;
const resolverMap = {};
resolverMap.Query = mergeObjects(queries);
resolverMap.Mutation = mergeObjects(mutations);
resolverMap.Subscription = mergeObjects(subscriptions);
resolvers.$list().forEach(type => (resolverMap[type] = resolvers[type]));
return new ApolloServer({
typeDefs: graphQlSchema,
resolvers: resolverMap,
context: ({ req, res }) => {
return { req, res, user: req.user };
},
formatError: graphqlFormatError,
playground: process.env.NODE_ENV !== "production",
});
}
@AlbertoPL我也遇到了这个问题。 结果证明是CORS 问题,而不是 Apollo 客户端。
当遇到网络错误或服务器端 CORS 配置错误时,fetch() 承诺将拒绝并返回 TypeError
确保在拒绝请求时返回正确的标头。 就我而言,我使用的是 API Gateway,而我的自定义授权方拒绝了请求,但没有附加Access-Control-Allow-Origin
标头。 我使用无服务器来管理部署,但如果使用它,它可以转换为 CloudFormation 模板: https: //serverless.com/blog/cors-api-gateway-survival-guide/#cors -with-custom-authorizers。
很高兴找到@chris-feist!
将Access-Control-Allow-Origin
标头添加到响应中使状态代码可用于 apollo-client
在 express.js 中,我只是做了以下事情:
res.set('Access-Control-Allow-Origin', '*').status(401).send()
使用 Access-Control-Allow-Origin 是否会带来潜在的安全风险? https://stackoverflow.com/questions/12001269/what-are-the-security-risks-of-setting-access-control-allow-origin
所以对于任何仍在寻找...
statusCode 值作为 networkError 的属性返回,并且在此处添加了 networkError 的更新类型: https :
由于networkError
现在是Error | ServerError | ServerParseError
的联合类型,Typescript 将只允许我们访问联合中所有类型共有的属性。 statusCode
在Error
类型上不存在。 如果没有 Typescript 抱怨,我们将无法通过networkError.statusCode
访问它; 我们需要使用类型保护或其他东西来区分类型。
Typescript 手册中推荐了几种方法: https: //www.typescriptlang.org/docs/handbook/advanced-types.html#type -guards-and-differentiating-types
出于我的目的,我使用了in
运算符来使事情正常进行。
onError: ({ networkError }: ErrorResponse) => {
if (
networkError &&
'statusCode' in networkError &&
networkError.statusCode === 401
) {
// perform logout stuff
}
},
@JoviDeCroock认为这个问题可以安全关闭?
@mrkrlli谢谢。 体面的解决方案。
最有用的评论
所以对于任何仍在寻找...
statusCode 值作为 networkError 的属性返回,并且在此处添加了 networkError 的更新类型: https :
由于
networkError
现在是Error | ServerError | ServerParseError
的联合类型,Typescript 将只允许我们访问联合中所有类型共有的属性。statusCode
在Error
类型上不存在。 如果没有 Typescript 抱怨,我们将无法通过networkError.statusCode
访问它; 我们需要使用类型保护或其他东西来区分类型。Typescript 手册中推荐了几种方法: https: //www.typescriptlang.org/docs/handbook/advanced-types.html#type -guards-and-differentiating-types
出于我的目的,我使用了
in
运算符来使事情正常进行。@JoviDeCroock认为这个问题可以安全关闭?