Apollo服务器和4xx状态代码

时间:2019-10-28 06:32:56

标签: hapijs apollo-server

当前,我的Apollo服务器(在HapiJS上运行)为每个请求(包括失败的请求)返回HTTP 200。

我希望GraphQL服务器为失败的请求返回HTTP 4xx。主要原因是我要为ELB设置监视。

我知道Apollo Server有一个引擎平台,但是我想使用当前的基础结构来实现它。

有什么想法可以做到吗?我试图为我的HapiJS服务器捕获“ onPreResponse”事件,但无法在此修改状态代码。

1 个答案:

答案 0 :(得分:1)

阅读此answer之后。这是通过修改hapiApollo.ts文件的hapijs插件graphqlHapi来解决的。

server.ts

import { makeExecutableSchema } from 'apollo-server';
import { ApolloServer, gql } from 'apollo-server-hapi';
import Hapi from 'hapi';
import { graphqlHapi } from './hapiApollo';

const typeDefs = gql`
  type Query {
    _: String
  }
`;
const resolvers = {
  Query: {
    _: () => {
      throw new Error('some error');
    },
  },
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const port = 3000;
async function StartServer() {
  const app = new Hapi.Server({ port });
  graphqlHapi.register(app, { path: '/graphql', graphqlOptions: { schema } });
  app.ext('onPreResponse', (request: any, h: any) => {
    const response = request.response;
    if (!response.isBoom) {
      return h.continue;
    }
    return h.response({ message: response.message }).code(400);
  });

  await app.start();
}

StartServer()
  .then(() => {
    console.log(`apollo server is listening on http://localhost:${port}/graphql`);
  })
  .catch((error) => console.log(error));

hapiApollo.ts

import Boom from 'boom';
import { Server, Request, RouteOptions } from 'hapi';
import { GraphQLOptions, runHttpQuery, convertNodeHttpToRequest } from 'apollo-server-core';
import { ValueOrPromise } from 'apollo-server-types';

export interface IRegister {
  (server: Server, options: any, next?: Function): void;
}

export interface IPlugin {
  name: string;
  version?: string;
  register: IRegister;
}

export interface HapiOptionsFunction {
  (request?: Request): ValueOrPromise<GraphQLOptions>;
}

export interface HapiPluginOptions {
  path: string;
  vhost?: string;
  route?: RouteOptions;
  graphqlOptions: GraphQLOptions | HapiOptionsFunction;
}

const graphqlHapi: IPlugin = {
  name: 'graphql',
  register: (server: Server, options: HapiPluginOptions, next?: Function) => {
    if (!options || !options.graphqlOptions) {
      throw new Error('Apollo Server requires options.');
    }
    server.route({
      method: ['GET', 'POST'],
      path: options.path || '/graphql',
      vhost: options.vhost || undefined,
      options: options.route || {},
      handler: async (request, h) => {
        try {
          const { graphqlResponse, responseInit } = await runHttpQuery([request, h], {
            method: request.method.toUpperCase(),
            options: options.graphqlOptions,
            query:
              request.method === 'post'
                ? // TODO type payload as string or Record
                  (request.payload as any)
                : request.query,
            request: convertNodeHttpToRequest(request.raw.req),
          });

          // add our custom error handle logic
          const graphqlResponseObj = JSON.parse(graphqlResponse);
          if (graphqlResponseObj.errors && graphqlResponseObj.errors.length) {
            throw new Error(graphqlResponseObj.errors[0].message);
          }

          const response = h.response(graphqlResponse);
          Object.keys(responseInit.headers as any).forEach((key) =>
            response.header(key, (responseInit.headers as any)[key]),
          );
          return response;
        } catch (error) {
          // handle our custom error
          if (!error.name) {
            throw Boom.badRequest(error.message);
          }

          if ('HttpQueryError' !== error.name) {
            throw Boom.boomify(error);
          }

          if (true === error.isGraphQLError) {
            const response = h.response(error.message);
            response.code(error.statusCode);
            response.type('application/json');
            return response;
          }

          const err = new Boom(error.message, { statusCode: error.statusCode });
          if (error.headers) {
            Object.keys(error.headers).forEach((header) => {
              err.output.headers[header] = error.headers[header];
            });
          }
          // Boom hides the error when status code is 500
          err.output.payload.message = error.message;
          throw err;
        }
      },
    });

    if (next) {
      next();
    }
  },
};

export { graphqlHapi };

现在,当GraphQL解析器抛出错误时,客户端将收到带有Http状态代码400的自定义响应,而不是带有GraphQL错误响应的200状态代码。

浏览器上的

常规

Request URL: http://localhost:3000/graphql
Request Method: POST
Status Code: 400 Bad Request
Remote Address: 127.0.0.1:3000
Referrer Policy: no-referrer-when-downgrade

响应正文为:{"message":"some error"}