如何使用 Vapor 3 处理多部分请求

How to handle multipart request with Vapor 3

我是一名 Vapor 初学者,我选择从 Vapor 3-rc 开始,因为它似乎打破了 Vaport 2 的变化。不幸的是,目前还没有完整的文档。

我目前正在尝试将一个简单的 txt 文件从 Postman 上传到我的 Vapor 3 本地服务器。

这是我的路线

let uploadController = FileUploadController()
router.post("uploadtxt", use: uploadController.uploadTXT)

和我的控制器

final class FileUploadController {
    func uploadTXT(_ req: Request) throws -> Future<String> {
        return try req.content.decode(MultipartForm.self).map(to: String.self, { form in
            let file = try form.getFile(named: "txtfile")
            return file.filename ?? "no-file"
        })
    }
}

首先,通过执行Postman请求,服务器returns:

{"error":true,"reason":"There is no configured decoder for multipart\/form-data; boundary=...}

通过调查源代码和limited documentation,看来我应该声明一个解码器来支持多部分传入请求。

所以我做到了:

var contentConfig = ContentConfig.default()
let decoder = FormURLDecoder()
contentConfig.use(decoder: decoder, for: .multipart)
services.register(contentConfig)

我使用 FormURLDecoder 因为它似乎是最接近我的需求的 class IMO,实施 BodyDecoder

现在它无限循环到 FormURLSingleValueDecoderfunc decode<T>(_ type: T.Type) throws -> T where T: Decodable,我被困在这里,只有很少的网络资源。

我在 Vapor slack 上结束了,这是查找一些信息和一些帮助的好地方。

解决方法很简单。与其使用 req.content.decode(MultipartForm.self),不如使用 MultipartForm.decode(from: req)(...删除的代码示例)

编辑:

AS @axello 说,MultipartForm 不存在了。我现在正在使用 req.content.decode(...) 方法来解析多部分数据。这个想法是创建一个对象来反映您的 HTML 表单输入。 Codable 会神奇地将数据映射到对象中。

例如,使用这种形式:

<form method="POST" action="upload" enctype="multipart/form-data" class="inputForm">
     <input type="name" name="filename">
     <input type="file" name="filedata">
     <input type="submit" name="GO" value="Send" class="send">
</form>

我创建了这个小结构

fileprivate struct MyFile: Content {
    var filename: String
    var filedata: Data
}

并且,在我的控制器中:

func uploadTXT(_ req: Request) throws -> Future<String> {
    return try req.content.decode(MyFile.self).map(to: String.self, { myFile in
        let filename = myFile.filename // this is the first input
        // ... and the second one:
        guard let fileContent = String(data: myFile.filedata, encoding: .utf8) else {
            throw Abort(.badRequest, reason: "Unreadable CSV file")
        }
        print(fileContent)
        return filename
    })
}