Лучший способ структурировать сервис в node.js / express? - PullRequest
0 голосов
/ 03 февраля 2020

Я начинаю перемещать логи c с маршрутов в приложении express в поставщика услуг. Один из этих маршрутов имеет дело не только с потоками, но и с тем, чтобы по окончании потока потребовалось еще несколько логик c. Вот пример маршрута express.

router.get("/file-service/public/download/:id", async(req, res) => {

try {

    const ID = req.params.id;

    FileProvider.publicDownload(ID, (err, {file, stream}) => {

        if (err) {

            console.log(err.message, err.exception);
            return res.status(err.code).send();

        } else {

            res.set('Content-Type', 'binary/octet-stream');
            res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
            res.set('Content-Length', file.metadata.size);

            stream.pipe(res).on("finish", () => {

                FileProvider.removePublicOneTimeLink(file);

            });
        }
    })

} catch (e) {
    console.log(e);
    res.status(500).send(e);
} 

})

А вот одна из функций внутри поставщика услуг.

this.publicDownload = async(ID, cb) => {

    const bucket = new mongoose.mongo.GridFSBucket(conn.db, {
        chunkSizeBytes: 1024 * 255,
    })

    let file = await conn.db.collection("fs.files")
    .findOne({"_id": ObjectID(ID)})

    if (!file|| !file.metadata.link) {

        return cb({
            message: "File Not Public/Not Found",
            code: 401,
            exception: undefined
        })

    } else {

        const password = process.env.KEY;
        const IV = file.metadata.IV.buffer

        const readStream = bucket.openDownloadStream(ObjectID(ID))

        readStream.on("error", (e) => {
            console.log("File service public download stream error", e);
        })

        const CIPHER_KEY = crypto.createHash('sha256').update(password).digest()        

        const decipher = crypto.createDecipheriv('aes256', CIPHER_KEY, IV);

        decipher.on("error", (e) => {
            console.log("File service public download decipher error", e);
        })

        cb(null, {
            file, 
            stream: readStream.pipe(decipher)
        })
    } 

}

Потому что не стоит передавать res или req в поставщика услуг (я полагаю, потому что модульного теста). Мне нужно вернуть поток внутри обратного вызова, оттуда я передаю этот поток в ответ, а также добавить событие on fini sh, чтобы удалить одноразовую ссылку для загрузки файла. Есть ли способ переместить больше этой логики c в поставщика услуг, не передавая в него res / req? Или я в этом все ошибаюсь?

1 Ответ

0 голосов
/ 03 февраля 2020

Есть ли способ переместить больше этой логики c в поставщика услуг, не передавая в него res / req?

Как мы уже обсуждали в комментариях, у вас есть операция загрузки, которая является частью бизнес-логики c и частью веб-логики c. Поскольку вы передаете ответ с помощью пользовательских заголовков, это не так просто, как «бизнес-логика c принесите мне данные, и я полностью справлюсь с ответом», как это делают многие операции с базами данных classi c.

Если вы собираетесь хранить их полностью раздельно, позволяя процессу загрузки максимально инкапсулировать, вам придется создать интерфейс с более высокой пропускной способностью между вашим поставщиком услуг и кодом Express, который знает о res объект, чем только один обратный вызов у ​​вас сейчас.

Прямо сейчас у вас поддерживается только одна операция, и она должна передавать поток по конвейеру. Но код загрузки действительно хочет указать информацию о типе и размере контента (это то, что известно внутри кода загрузки), и он хочет знать, когда выполняется поток записи, чтобы он мог выполнить свои логики очистки c. И что-то, что вы не показываете, - это правильная обработка ошибок, если во время потоковой передачи данных клиенту произошла ошибка (в том числе и с надлежащей очисткой).

Если вы хотите переместить больше кода в загрузчик, по сути, вам нужно было бы создать небольшой интерфейс, позволяющий коду службы выполнять более одной операции с ответом, но без фактического объекта ответа. Этот интерфейс не должен быть потоком полного ответа. На нем могут быть только методы для получения уведомлений о завершении потока, запуска потоковой передачи, установки заголовков и т. Д. c ...

Как я уже говорил в комментариях, вам придется решить, если это на самом деле делает код проще или нет. Правила дизайна не являются абсолютными. Это вещи, которые следует учитывать при выборе дизайна. Они не должны вести вас в направлении, которое дает вам код, который значительно сложнее, чем если бы вы сделали другой выбор дизайна.

...