Apollo-link: apollo-link-error 处的 networkError 没有 status 属性

创建于 2017-12-02  ·  35评论  ·  资料来源: apollographql/apollo-link

作为主题。 谢谢

const errorLink = onError(({ networkError }) => {
  if (networkError.status === 401) {
    logout();
  }
})

screen shot 2017-12-03 at 4 49 20 am

最有用的评论

所以对于任何仍在寻找...

statusCode 值作为 networkError 的属性返回,并且在此处添加了 networkError 的更新类型: https :

由于networkError现在是Error | ServerError | ServerParseError的联合类型,Typescript 将只允许我们访问联合中所有类型共有的属性。 statusCodeError类型上不存在。 如果没有 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认为这个问题可以安全关闭?

所有35条评论

据我所知,如果没有响应(例如服务器关闭),就会发生“无法获取”。 那是因为您不会看到任何状态代码。 如果我错了,请纠正我。

但我认为在这种情况下,只拥有一条短信并不是最好的主意——应该有一些更容易处理的东西(比如错误代码)。

我确实在 chrome Inspector 的网络标签中看到请求正在发送和输入 (401)。

我认为这与#218有关(不确定..)

也有这个问题。 networkError.statusnetworkError.statusCode丢失且响应对象丢失。 无法在网络层处理 HTTP 状态代码。

我目前也有这个问题。 它应该在最新版本中得到修复,但我仍然遇到这个问题。

对此@jbaxleyiii有何想法?

我很遗憾听到您@akrigline@bogdansoare仍然遇到此问题! 这绝对应该被修复。

我现在正在处理 #364。 同时,您能否创建或修改此代码沙盘以重现错误? 或者甚至更好地打开一个测试失败的 PR?

任何新闻?

同样有这个问题,响应对象是空的,并且 networkError 对象上没有 statusCode。

我什至没有得到networkError属性:
screen shot 2018-05-07 at 13 01 56

编辑:很可能#475

我已经通过忽略Error类型解决了这个问题,但这是一个完全临时的解决方案,我们必须等待未来的修复。 @埃文斯

这是这个问题!

我们写的关于它们的networkError的当前不是真正的服务器端错误。 总之,在这些情况下,我们根本不会有statusCode ,事实上,我们需要在 graphql 服务器上创建或接收真正的网络错误。 (_如果您只是与服务器的通信问题,则无法访问statusCode _)。

screen shot 2018-05-07 at 20 38 09

字体错误仍然存​​在!

如果您使用的是 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 状态码的服务器错误,现在可以访问此状态码:

screen shot 2018-05-07 at 20 28 03

这是我的 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);
  }
});

在服务器上我使用koakoa-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-httpapollo-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 块。 到那时,错误已经设置为“无法获取”。 不存在状态代码。

我们应该在这里检查是否出错:

https://github.com/apollographql/apollo-link/blob/master/packages/apollo-link-http-common/src/index.ts#L118

这个周末我会尝试制作一个复制品,因为工作中的自动取款机日程安排非常繁忙。

编辑:

主要问题是每次我测试时我都使用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 将只允许我们访问联合中所有类型共有的属性。 statusCodeError类型上不存在。 如果没有 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谢谢。 体面的解决方案。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

NicholasLYang picture NicholasLYang  ·  4评论

Nickersoft picture Nickersoft  ·  3评论

vjpr picture vjpr  ·  3评论

tim-soft picture tim-soft  ·  4评论

steffenmllr picture steffenmllr  ·  4评论