Попытка получить метаданные URL-адреса в Express/NodeJS

У меня есть ресурс, содержащий поле URL, введенное пользователем. Я пытаюсь использовать этот пакет: https://github.com/mozilla/page-metadata-parser, чтобы получить заголовок и описание, связанные с URL-адресом, и сохранить их в базе данных при создании.

Я добавил код, смоделированный в документации пакета, в почтовый запрос в Express, и ошибок нет, новая закладка создана, но значения метаданных не возвращаются.

Вот моя модель:

const mongoose = require('mongoose');

const { Schema } = mongoose;

const BookmarksSchema = new Schema({
  userId: {
    type: Schema.Types.ObjectId,
    required: true
  },
  url: {
    type: String,
    trim: true,
    required: true
  },

...

  title: {
    type: String,
    trim: true,
    required: false
  },
  description: {
    type: String,
    trim: true,
    required: false
  }
});

mongoose.model('Bookmarks', BookmarksSchema);

мой метод создания:

const mongoose = require('mongoose');
const passport = require('passport');
const router = require('express').Router();
const auth = require('../auth');
const Bookmarks = mongoose.model('Bookmarks');

router.post('/', auth.required, (req, res, next) => {
  const userId = req.user.id;
  const bookmark = req.body.bookmark;

  if (!bookmark.url) {
    return res.status(422).json({
      errors: {
        url: 'is required',
      },
    });
  }

  const { getMetadata } = require('page-metadata-parser');
  const domino = require('domino');

  const url = bookmark.url;
  const response = fetch(url);
  const html = response.text();
  const doc = domino.createWindow(html).document;
  const metadata = getMetadata(doc, url);

  bookmark.userId = userId;
  bookmark.title = metadata.title;
  bookmark.description = metadata.description;

  const finalBookmark = new Bookmarks(bookmark);

  return finalBookmark.save()
    .then(() => res.json({ bookmark: finalBookmark }));
});

и пакет.json:

{
  "name": "server",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "dependencies": {
    "body-parser": "^1.18.3",
    "cors": "^2.8.5",
    "domino": "^2.1.3",
    "errorhandler": "^1.5.0",
    "express": "^4.16.4",
    "express-jwt": "^5.3.1",
    "express-session": "^1.15.6",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^5.4.20",
    "morgan": "^1.9.1",
    "page-metadata-parser": "^1.1.3",
    "passport": "^0.4.0",
    "passport-local": "^1.0.0",
    "path": "^0.12.7"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon app"
  },
  "author": "",
  "license": "ISC"
}

Взглянув на документацию, я увидел, что вызову fetch(url) предшествует ключевое слово await. Попробуйте поместить это в свой код и запустить его снова. Я готов поспорить, что fetch — это асинхронный метод, который требует, чтобы вы использовали await для выполнения обещания, прежде чем двигаться дальше в коде.

Michael Platt 10.04.2019 18:07

Сначала я так и сделал, но куда мне поставить звонок async?

user2799827 10.04.2019 18:10

Ключевое слово async будет использоваться при объявлении анонимной функции обратного вызова в маршрутизаторе. Похоже на линию 7 прямо перед (req, res, next). Вставьте это ключевое слово и посмотрите, работает ли оно.

Michael Platt 10.04.2019 18:13

Это работает. Спасибо! Я изменил запрос на публикацию: router.post('/', auth.required, async function (req, res, next) {. Затем мне пришлось добавить модуль node-fetch. Вы мне очень помогли. Если вы сделаете это ответом, я приму его.

user2799827 10.04.2019 18:33

Рад, что это работает для вас. Пошел дальше и опубликовал немного более подробный ответ с большим количеством кода, чтобы позже он помог большему количеству людей, которые его посмотрят. Удачного кодирования!!

Michael Platt 10.04.2019 19:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
4 344
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Публикация ответа здесь, чтобы мы могли отметить ответ как правильный.

Ошибка возникла из-за того, что вызов fetch() был асинхронным вызовом и ключевое слово await не использовалось. Пример на сайте NPM можно найти здесь:

https://www.npmjs.com/package/page-metadata-parser

Показывает, что они использовали await в вызове fetch(). Чтобы использовать await анонимную функцию обратного вызова, перед функцией, которая начинается с (req, res, next), должно стоять ключевое слово async. Тогда вызов должен выглядеть так:

router.post('/', auth.required, async (req, res, next) => {
     // Do your stuff here as before.
     const url = bookmark.url;
     const response = await fetch(url);
     const html = response.text();
     const doc = domino.createWindow(html).document;
     const metadata = getMetadata(doc, url);
     // Finish stuff here.
});

Теперь ответ заполняется, и программа ожидает завершения вызова fetch, прежде чем двигаться вперед, таким образом заполняя оставшиеся переменные и получая возможность получить метаданные.

Другие вопросы по теме