GraphQL文件上传的拒绝和错误处理

时间:2019-12-16 11:18:29

标签: file-upload error-handling upload graphql express-graphql

我有一个带有可接收单个文件的解析器的GraphQL端点。解析程序包括对接收文件的简单验证。

问题是,当验证失败时,无法立即将错误返回到前端,因为抛出错误不会强制上载请求被中断。

一个过度简化的例子:

fileUpload: async (parent, { file, otherdata }) => {
    const isValid = (some logic here)
    if(!isValid)
        throw new ApolloError('upload parameters not valid')

    //No need to get this far,
    //we could have rejected the request already by examining otherdata,
    //or the stream could be already created for max time utilization

    const { createReadStream, filename, mimetype, encoding } = await file;
    const readStream = await createReadStream()
    ...
 }

预期的行为:解析器返回通常的{errors:[],data:null}对象-或错误本身-取决于error-policy选项。

实际行为:该错误在后端引发,但请求在前端仍处于待处理状态。

我已经尝试以下操作失败了:

  • 无论如何都要初始化readStream并对其调用.destroy()。结果是读取流停止并且请求永远保持未决状态。
  • 在解析器上下文中包含请求对象(也尝试了响应对象),并在其上调用.destroy()。这样就结束了请求,但导致前端出现网络错误,没有机会进行错误特定的处理。

一些说明:

  • 显然也有客户端验证,但这并不需要服务器端验证。
  • 等待上传完成然后抛出错误显然是可行的,但这并不是时间和带宽的明智选择。

我了解使用GraphQL上传文件是边界支持的功能,但是在这种情况下,我们所谈论的是相当基本的操作。

任何建议,我将不胜感激!

1 个答案:

答案 0 :(得分:0)

事实证明,这是默认的表达行为,与GraphQL接口绝对无关。 该解决方案的纯快速版本可以在here中找到。

请求和响应对象应传递到解析器上下文:

const apollo = new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ req, res }) => {
        return {
            req,
            res
        };
    }
});

然后根据示例,在解析器中:

fileUpload: async (parent, { file, otherdata }, {req, res}) => {
    const isValid = (some logic here)
    if(!isValid){
        res.send(403).send("Some message")
        // Throwing ApolloError could also work,
        // in which case response object would not be required, but not tested.
        // throw new ApolloError('upload parameters not valid')
        return req.destroy()
    }
        

    //No need to get this far,
    //we could have rejected the request already by examining otherdata,
    //or the stream could be already created for max time utilization

    const { createReadStream, filename, mimetype, encoding } = await file;
    const readStream = await createReadStream()
    ...
 }