You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails GraphQL API:RSpec测试突变返回Null,GraphiQL却正常(Header问题?)

问题分析与解决方案

从错误信息Cannot return null for non-nullable field CreateSurveyPayload.user可以明确,问题根源是突变执行时无法获取到当前授权用户,导致CreateSurveyPayload的user字段(非空约束)返回null,进而触发整个createSurvey结果为null。结合你描述的"GraphiQL带授权头正常、不带则返回null"的现象,说明测试中的授权流程未正确生效。

排查与修复步骤

1. 修正RSpec测试的请求头格式

Rails在处理测试请求时,会将标准HTTP头转换为HTTP_前缀加下划线的键名。你当前使用的'Authorization'键可能未被正确识别,需改为'HTTP_AUTHORIZATION':

post '/graphql', headers: { 'HTTP_AUTHORIZATION': "Bearer #{token}" }, params: { query: mutation_query }

2. 验证GraphQL上下文的用户解析逻辑

确保你的GraphQLController正确从请求头解析token并将用户注入GraphQL上下文:

# app/controllers/graphql_controller.rb
def execute
  variables = prepare_variables(params[:variables])
  query = params[:query]
  operation_name = params[:operationName]
  
  context = {
    current_user: current_user
  }

  result = YourSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
  render json: result
end

private

def current_user
  auth_header = request.headers['HTTP_AUTHORIZATION'] || request.headers['Authorization']
  return nil unless auth_header

  token = auth_header.split('Bearer ').last
  decoded_payload = JWT.decode(token, 'segredo', true, algorithm: 'HS256').first
  User.find(decoded_payload['user_id'])
rescue JWT::DecodeError, ActiveRecord::RecordNotFound
  nil
end

注意:要确保解码token使用的密钥'segredo'与测试中生成token的密钥完全一致,同时处理解码失败或用户不存在的异常,避免上下文注入nil。

3. 检查createSurvey突变的resolve逻辑

确认突变的resolve方法依赖上下文的current_user,并在用户未授权时抛出明确错误:

class Mutations::CreateSurvey < Mutations::BaseMutation
  field :user, Types::UserType, null: false
  field :survey, Types::SurveyType, null: true
  field :questions, [Types::QuestionType], null: true
  field :errors, [String], null: false

  argument :input, Types::CreateSurveyInput, required: true

  def resolve(input:)
    current_user = context[:current_user]
    raise GraphQL::ExecutionError, "Unauthorized access" unless current_user

    # 此处编写创建Survey及关联Question的逻辑
    survey = current_user.surveys.create!(title: input[:title], amt_questions: input[:amt_questions])
    # ... 创建questions的逻辑

    {
      user: current_user,
      survey: survey,
      questions: survey.questions,
      errors: []
    }
  end
end

这里必须保证current_user存在时才执行创建逻辑,否则抛出授权错误,避免因user字段返回null触发非空约束错误。

4. 测试中验证token有效性

在测试中添加token解码验证,确保生成的token能正确解析出用户ID:

token = JWT.encode({ user_id: user.id }, 'segredo', 'HS256')
decoded_token = JWT.decode(token, 'segredo', true, algorithm: 'HS256')
expect(decoded_token.first['user_id']).to eq(user.id)

额外排查点

  • 确认测试环境中JWT gem的版本与开发环境一致,避免解码逻辑差异。
  • 检查用户模型是否有role字段的验证逻辑,确保测试中创建的admin角色用户符合授权要求(如果突变有角色权限限制)。

内容的提问来源于stack exchange,提问作者José Sávio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 07:45:15