Поиск по сайту:

Как создать безопасный сокращатель URL-адресов в NodeJS с помощью NestJS


Автор выбрал программу Write for DOnations.

Введение

URL (аббревиатура от Uniform Resource Locator) – это адрес уникального ресурса в Интернете. Поскольку URL-адрес уникален, никакие два ресурса не могут иметь одинаковый URL-адрес.

Длина и сложность URL различаются. URL-адрес может быть таким коротким, как http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk. Сложные URL-адреса могут быть неприглядными, вызывать проблемы с поисковой оптимизацией (SEO) и негативно влиять на маркетинговые планы. Сокращатели URL-адресов сопоставляют длинный URL-адрес с более коротким URL-адресом и перенаправляют пользователя на исходный URL-адрес при использовании короткого URL-адреса.

В этом руководстве вы создадите средство сокращения URL-адресов с помощью сервиса. Затем вы создадите обработчики маршрутов, чтобы упростить запросы на сокращение и перенаправление.

Предпосылки

Чтобы следовать этому руководству, вам понадобятся:

  • Локальная среда разработки для учебника «Как установить Node.js и создать локальную среду разработки», которая подходит для вашей системы.
  • Командная строка NestJS, установленная в вашей системе, которую вы настроите на шаге 1, и знакомство с NestJS. Ознакомьтесь с разделом «Начало работы с NestJS».
  • Ознакомьтесь с серией статей How To Code in TypeScript, чтобы узнать больше.

Шаг 1 — Подготовка среды разработки

На этом шаге вы настроите все необходимое, чтобы приступить к реализации логики сокращения URL-адресов. Вы установите NestJS глобально, сгенерируете новый шаблон приложения NestJS, установите зависимости и создадите модуль, службу и контроллер вашего проекта.

Во-первых, вы установите Nest CLI глобально, если вы не установили его ранее. Вы будете использовать этот интерфейс командной строки для создания каталога вашего проекта и необходимых файлов. Выполните следующую команду, чтобы установить интерфейс командной строки Nest:

  1. npm install -g @nestjs/cli

Флаг -g глобально установит Nest CLI в вашей системе.

Вы увидите следующий вывод:

  1. Output
    ...
  2. added 249 packages, and audited 250 packages in 3m
  3. 39 packages are looking for funding
  4. run npm fund for details
  5. found 0 vulnerabilities

Затем вы будете использовать команду new для создания проекта и создания необходимых шаблонных начальных файлов:

  1. nest new URL-shortener

Вы увидите следующий вывод:

  1. Output
    ...
  2. ⚡ We will scaffold your app in a few seconds..
  3. CREATE url-shortener/.eslintrc.js (631 bytes)
  4. CREATE url-shortener/.prettierrc (51 bytes)
  5. CREATE url-shortener/nest-cli.json (118 bytes)
  6. CREATE url-shortener/package.json (2002 bytes)
  7. CREATE url-shortener/README.md (3339 bytes)
  8. CREATE url-shortener/tsconfig.build.json (97 bytes)
  9. CREATE url-shortener/tsconfig.json (546 bytes)
  10. CREATE url-shortener/src/app.controller.spec.ts (617 bytes)
  11. CREATE url-shortener/src/app.controller.ts (274 bytes)
  12. CREATE url-shortener/src/app.module.ts (249 bytes)
  13. CREATE url-shortener/src/app.service.ts (142 bytes)
  14. CREATE url-shortener/src/main.ts (208 bytes)
  15. CREATE url-shortener/test/app.e2e-spec.ts (630 bytes)
  16. CREATE url-shortener/test/jest-e2e.json (183 bytes)
  17. ? Which package manager would you ❤️ to use? (Use arrow keys)
  18. > npm
  19. yarn
  20. pnpm

Выберите npm.

Вы увидите следующий вывод:

  1. Output
    √ Installation in progress... ☕
  2. 🚀 Successfully created project url-shortener
  3. 👉 Get started with the following commands:
  4. $ cd url-shortener
  5. $ npm run start
  6. Thanks for installing Nest 🙏
  7. Please consider donating to our open collective
  8. to help us maintain this package.
  9. 🍷 Donate: https://opencollective.com/nest

Перейдите в созданный каталог проекта:

  1. cd url-shortener

Вы будете запускать все последующие команды в этом каталоге.

Примечание. Интерфейс командной строки NestJS создает файлы app.controller.ts, app.controller.spec.ts и app.service.ts, когда вы создать новый проект. Поскольку они вам не понадобятся в этом руководстве, вы можете либо удалить их, либо проигнорировать.

Далее вы установите необходимые зависимости.

Для этого руководства требуется несколько зависимостей, которые вы установите с помощью диспетчера пакетов NodeJS по умолчанию Nano-ID.

TypeORM — это объектно-реляционный преобразователь, облегчающий взаимодействие между приложением TypeScript и реляционной базой данных. Этот ORM без проблем работает с NestJS благодаря специальному пакету NestJS @nestjs/typeorm. Вы будете использовать эту зависимость с собственным пакетом NestJS typeorm для взаимодействия с базой данных SQLite.

Выполните следующую команду, чтобы установить TypeORM и его специальный пакет NestJS:

  1. npm install @nestjs/typeorm typeorm

SQLite — это библиотека, которая реализует небольшой, быстрый, автономный механизм базы данных SQL. Вы будете использовать эту зависимость в качестве базы данных для хранения и извлечения сокращенных URL-адресов.

Выполните следующую команду, чтобы установить SQLite:

  1. npm install sqlite3

Пакет class-validator содержит декораторы, используемые для проверки данных в NestJS. Вы будете использовать эту зависимость с вашим объектом передачи данных для проверки данных, отправленных в ваше приложение.

Выполните следующую команду, чтобы установить class-validator:

  1. npm install class-validator

Пакет class-transformer позволяет преобразовывать обычные объекты в экземпляры класса и наоборот. Вы будете использовать эту зависимость с class-validator, так как она не может работать одна.

Выполните следующую команду, чтобы установить class-transformer:

  1. npm install class-transformer

Nano-ID — это безопасный генератор уникальных строковых идентификаторов с поддержкой URL-адресов. Вы будете использовать эту зависимость для создания уникального идентификатора для каждого ресурса URL.

Выполните следующую команду, чтобы установить Nano-ID:

  1. npm install nanoid@^3.0.0

Примечание. Версии Nano-ID выше 3.0.0 отключили поддержку модулей CommonJS. Эта проблема может вызвать ошибку в вашем приложении, поскольку код JavaScript, созданный компилятором TypeScript, по-прежнему использует систему модулей CommonJS.

После того, как вы установили необходимые зависимости, вы сгенерируете модуль, службу и контроллер проекта с помощью интерфейса командной строки Nest. Модуль будет организовывать ваш проект, сервис будет обрабатывать всю логику для сокращения URL-адресов, а контроллер будет обрабатывать маршруты.

Запустите следующую команду, чтобы сгенерировать модуль:

  1. nest generate module url

Вы увидите следующий вывод:

Output
CREATE src/url/url.module.ts (80 bytes) UPDATE src/app.module.ts (304 bytes)

Затем выполните следующую команду для создания службы:

  1. nest generate service url --no-spec

Флаг --no-spec указывает интерфейсу командной строки Nest создавать файлы без их тестовых файлов. В этом руководстве вам не понадобятся тестовые файлы.

Вы увидите следующий вывод:

Output
CREATE src/url/url.service.ts (87 bytes) UPDATE src/url/url.module.ts (151 bytes)

Затем выполните следующую команду, чтобы сгенерировать контроллер:

  1. nest generate controller url --no-spec

Вы увидите следующий вывод:

Output
CREATE src/url/url.controller.ts (95 bytes) UPDATE src/url/url.module.ts (233 bytes)

На этом шаге вы создали свое приложение и большинство файлов, необходимых для разработки. Далее вы подключите свое приложение к базе данных.

Шаг 2 — Подключение вашего приложения к базе данных

На этом шаге вы создадите объект для моделирования ресурса URL в вашей базе данных. сущность — это файл, содержащий необходимые свойства хранимых данных. Вы также создадите репозиторий в качестве уровня доступа между вашим приложением и его базой данных.

Используя nano или предпочитаемый вами текстовый редактор, создайте и откройте в папке src/url файл с именем url.entity.ts:

  1. nano src/url/url.entity.ts

Этот файл будет содержать объект для моделирования ваших данных.

Затем в файле src/url/url.entity.ts добавьте следующий код Typescript:

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Url {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    urlCode: string;

    @Column()
    longUrl: string;

    @Column()
    shortUrl: string;
}

Сначала вы импортируете декораторы Entity, Column и PrimaryGeneratedColumn из typeorm.

Код создает и экспортирует класс Url с аннотацией декоратора Entity, который помечает класс как сущность.

Каждое свойство указывается и аннотируется соответствующими декораторами: PrimaryGeneratedColumn для идентификатора и Column для остальных свойств. PrimaryGeneratedColumn — это декоратор, который автоматически генерирует значение для свойств, которые он аннотирует. TypeOrm будет использовать его для создания идентификатора для каждого ресурса. Столбец — это декоратор, который добавляет свойство, которое он аннотирует, как столбец в базе данных.

Свойства, которые должны храниться в базе данных, включают следующее:

  • id — это первичный ключ для таблицы базы данных.
  • urlCode – это уникальный идентификатор, сгенерированный пакетом nanoid, который будет использоваться для идентификации каждого URL-адреса.
  • longUrl — URL-адрес, отправленный вашему приложению для сокращения.
  • shortUrl – сокращенный URL-адрес.

Сохраните и закройте файл.

Далее вы создадите соединение между вашим приложением и вашей базой данных.

Сначала откройте src/app.module.ts в nano или предпочитаемом вами текстовом редакторе:

  1. nano src/app.module.ts

Затем добавьте выделенные строки в файл:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Url } from './url/url.entity';
import { UrlModule } from './url/url.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'URL.sqlite',
      entities: [Url],
      synchronize: true,
    }),
    UrlModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

Сначала импортируйте TypeOrmModule из @nestjs/typeorm и Url из ./url/url.entity. У вас могут остаться строки, связанные с AppController и AppService. Если оставить их в файле, это не повлияет на остальную часть урока.

В массиве imports вызовите метод forRoot для TypeOrmModule, чтобы совместно использовать подключение для всех модулей вашего приложения. Метод forRoot принимает в качестве аргумента объект конфигурации.

Объект конфигурации содержит свойства, которые создают соединение. Эти свойства включают следующее:

  • Свойство type обозначает тип базы данных, с которой вы используете TypeOrm для взаимодействия. В данном случае установлено значение sqlite.
  • Свойство database обозначает предпочтительное имя для вашей базы данных. В данном случае установлено значение URL.sqlite.
  • Свойство entities представляет собой массив всех объектов в вашем проекте. В этом случае у вас есть только один объект, указанный как Url внутри массива.
  • Параметр synchronize автоматически синхронизирует таблицы вашей базы данных с вашей сущностью и обновляет таблицы каждый раз, когда вы запускаете код. В этом случае установлено значение true.

Примечание. Установка для synchronize значения true идеальна только в среде разработки. В рабочей среде всегда следует устанавливать значение false, так как это может привести к потере данных.

Сохраните и закройте файл.

Затем вы создадите репозиторий, который будет действовать как уровень доступа между вашим приложением и вашей базой данных. Вам нужно будет подключить свою сущность к ее родительскому модулю, и это соединение позволит Nest и TypeOrm автоматически создать репозиторий.

Откройте src/url/url.module.ts:

  1. nano src/url/url.module.ts

В существующий файл добавьте выделенные строки:

import { Module } from '@nestjs/common';
import { UrlService } from './url.service';
import { UrlController } from './url.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Url } from './url.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Url])],
  providers: [UrlService],
  controllers: [UrlController],
})
export class UrlModule {}

Вы создаете массив imports внутри декоратора Module, куда вы импортируете TypeOrmModule из @nestjs/typeorm и URL из ./url.entity. Внутри массива imports вы вызываете метод forFeature для TypeOrmModule. Метод forFeature принимает массив сущностей в качестве аргумента, поэтому вы передаете сущность Url.

Сохраните и закройте файл.

Nest и TypeOrm создадут репозиторий за кулисами, который будет действовать как уровень доступа между вашим сервисом и базой данных.

На этом шаге вы подключили свое приложение к базе данных. Теперь вы готовы реализовать логику сокращения URL-адресов.

Шаг 3 — Реализация сервисной логики

На этом шаге вы реализуете логику службы двумя способами. Первый метод, shortenUrl, будет содержать всю логику сокращения URL. Второй метод, redirect, будет содержать всю логику для перенаправления пользователя на исходный URL. Вы также создадите объект передачи данных для проверки данных, поступающих в ваше приложение.

Предоставление сервисного доступа к репозиторию

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

Сначала откройте src/url/url.service.ts:

  1. nano src/url/url.service.ts

Добавьте следующие выделенные строки в существующий файл:

import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}
}

Вы импортируете Repository из typeorm, InjectRepository из @nestjs/typeorm и Url. из ./url.entity.

В своем классе UrlService вы создаете конструктор. Внутри конструктора вы объявляете частную переменную repo в качестве параметра. Затем вы назначаете тип Repository для repo с общим типом Url. Вы аннотируете переменную repo декоратором InjectRepository и передаете Url в качестве аргумента.

Сохраните и закройте файл.

Теперь у вашего сервиса есть доступ к вашему репозиторию через переменную repo. На нем будут вызываться все запросы к базе данных и методы TypeOrm.

Далее вы создадите асинхронный метод shortenUrl. Метод примет URL-адрес в качестве аргумента и вернет сокращенный URL-адрес. Чтобы убедиться, что данные, переданные в метод, действительны, вы будете использовать объект передачи данных вместе с пакетами class-validator и class-transformer для проверки данных. .

Создание объекта передачи данных

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

Сначала создайте папку dtos (объекты передачи данных) в папке url:

  1. mkdir src/url/dtos

Затем создайте файл с именем url.dto.ts в этой папке:

  1. nano src/url/dtos/url.dto.ts

Добавьте в новый файл следующий код:

import { IsString, IsNotEmpty } from 'class-validator';

export class ShortenURLDto {
  @IsString()
  @IsNotEmpty()
  longUrl: string;
}

Вы импортируете декораторы IsString и IsNotEmpty из class-validator. Затем вы создаете и экспортируете класс ShortenURLDto. Внутри класса ShortenURLDto вы создаете свойство longUrl и назначаете ему тип string.

Вы также аннотируете свойство longUrl декораторами IsString и IsNotEmpty. Аннотирование свойства longUrl с помощью этих декораторов гарантирует, что longUrl всегда будет строкой и не будет пустым.

Сохраните и закройте файл.

Затем откройте файл src/main.ts:

  1. nano src/main.ts

Добавьте выделенные фрагменты кода в существующий файл:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
  await app.listen(3000);
}
bootstrap();

Вы импортируете ValidationPipe, который использует пакет class-validator для применения правил проверки ко всем данным, поступающим в ваше приложение.

Затем вы вызываете метод useGlobalPipes в своем экземпляре приложения (app) и передаете экземпляр ValidationPipe с объектом параметров, где whitelist имеет значение true. Метод useGlobalPipes связывает ValidationPipe на уровне приложения, гарантируя, что все маршруты защищены от некорректных данных. Установка для свойства whitelist значения true удаляет проверенные (возвращенные) объекты свойств, которые не указаны в вашем DTO.

Сохраните и закройте файл.

Затем вы импортируете объект передачи данных в файл url.service.ts и применяете его к методу shortenUrl.

Создание метода ShortenUrl

Метод shortenUrl обрабатывает большую часть логики сокращения URL. Он будет принимать параметр url типа ShortenURLDto.

Сначала откройте файл src/url/url.service.ts:

  1. nano src/url/url.service.ts

Добавьте выделенные строки в файл:

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';
...

Сначала вы импортируете NotFoundExeception, BadRequestException и UnprocessableEntityException из @nestjs/common, потому что вы будете использовать их для ошибок умение обращаться. Затем вы импортируете {nanoid} из nanoid и isURL из class-validator. isURL будет использоваться для подтверждения того, что предоставленный longUrl является действительным URL-адресом. Наконец, вы импортируете ShortenURLDto из ./dtos/url.dto для проверки данных.

Затем добавьте следующее в класс UrlService под constructor:

...
async shortenUrl(url: ShortenURLDto) {}

Затем добавьте следующий код в метод shortenUrl:

...
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }

В приведенном выше блоке кода longUrl был деструктурирован из объекта url. Затем с помощью метода isURL проверяется, является ли longUrl допустимым URL-адресом.

urlCode создается с использованием nanoid. По умолчанию nanoid генерирует уникальную строку из двадцати одного символа. Передайте желаемую длину в качестве аргумента, чтобы переопределить поведение по умолчанию. В этом случае вы передаете значение 10, потому что хотите, чтобы URL-адрес был как можно короче.

Затем определяется базовый URL. Базовый URL — это постоянный корень адреса вашего веб-сайта. В разработке это ваш локальный хост-сервер; в производстве это ваше доменное имя. В этом руководстве в примере кода используется localhost.

Блок try-catch будет содержать весь код для взаимодействия с базой данных для обработки ошибок.

Двукратное сокращение URL-адреса может привести к дублированию данных, поэтому в базе данных выполняется поисковый запрос, чтобы проверить, существует ли URL-адрес. Если он существует, будет возвращен его shortUrl; в противном случае код продолжает сокращать его. Если в базе данных не найдена запись URL-адреса, короткий URL-адрес создается путем объединения baseURL и urlCode.

Затем создается экземпляр сущности url с urlCode, longUrl и shortUrl. Экземпляр url сохраняется в базе данных путем вызова метода save в repo и передачи экземпляра в качестве аргумента. Затем возвращается shortUrl.

Наконец, если возникает ошибка, она регистрируется на консоли в блоке catch и выдается сообщение UnprocessableEntityException.

Вот как теперь будет выглядеть ваш файл url.service.ts:

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}

  async shortenUrl(url: ShortenURLDto) {
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }
  }
}

Сохраните файл.

Здесь вы настраиваете первую часть логики сокращения URL. Далее вы реализуете метод redirect в своей службе.

Создание метода перенаправления

Метод redirect будет содержать логику, которая перенаправляет пользователей на длинный URL.

В файле src/url/url/service.ts добавьте следующий код в конец класса UrlService, чтобы реализовать перенаправление. метод:

...
  async redirect(urlCode: string) {
    try {
      const url = await this.repo.findOneBy({ urlCode });
      if (url) return url;
    } catch (error) {
      console.log(error);
      throw new NotFoundException('Resource Not Found');
    }
  }

Метод redirect принимает urlCode в качестве аргумента и пытается найти в базе данных ресурс с соответствующим urlCode. Если ресурс существует, он вернет ресурс. В противном случае выдается ошибка NotFoundException.

Ваш заполненный файл url.service.ts теперь будет выглядеть следующим образом:

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}

  async shortenUrl(url: ShortenURLDto) {
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }
  }

  async redirect(urlCode: string) {
    try {
      const url = await this.repo.findOneBy({ urlCode });
      if (url) return url;
    } catch (error) {
      console.log(error);
      throw new NotFoundException('Resource Not Found');
    }
  }
}

Сохраните и закройте файл.

Ваша логика сокращения URL-адреса теперь дополнена двумя методами: один для сокращения URL-адреса, а другой для перенаправления сокращенного URL-адреса на исходный URL-адрес.

На следующем шаге вы реализуете обработчик маршрута для этих двух методов в своем классе контроллера.

Шаг 4 — Реализация логики контроллера

На этом шаге вы создадите два обработчика маршрута: обработчик маршрута POST для обработки запросов на сокращение и обработчик маршрута GET для обработки запросов на перенаправление.

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

Сначала откройте файл src/url/url.controller.ts:

  1. nano src/url/url.controller.ts

Добавьте выделенные строки в файл:

import { Controller } from '@nestjs/common';
import { UrlService } from './url.service';

@Controller('url')
export class UrlController {
  constructor(private service: UrlService) {}
}

Сначала вы импортируете UrlService из ./url.service. Затем в классе контроллера вы объявляете конструктор и инициализируете частную переменную service в качестве параметра. Вы назначаете service тип UrlService.

Декоратор Controller в настоящее время имеет строку url в качестве аргумента, что означает, что контроллер будет обрабатывать только запросы, сделанные на localhost/3000/url/. маршрут. Это приводит к ошибке в логике сокращения URL-адресов, поскольку сокращенные URL-адреса включают базовый URL-адрес (localhost:3000) и код URL-адреса (wyt4_uyP-Il), которые объединяются в сформируйте новый URL-адрес (localhost:3000/wyt4_uyP-Il). Следовательно, этот контроллер не может обрабатывать запросы, а укороченные ссылки возвращают ошибку 404 Not Found. Чтобы решить эту проблему, удалите аргумент url из декоратора Controller и реализуйте отдельные маршруты для каждого обработчика.

После удаления аргумента url ваш UrlController будет выглядеть следующим образом:

@Controller()
export class UrlController {
  constructor(private service: UrlService) {}
}

Оставаясь в файле src/url/url.controller.ts, добавьте выделенные элементы в оператор import:

import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
import { UrlService } from './url.service';
import { ShortenURLDto } from './dtos/url.dto';

Вы импортируете Body, Get, Param, Post и Res из @nestjs/common и ShortenURLDto из ./dtos/url.dto. Декораторы будут дополнительно определены по мере добавления в этот файл.

Затем добавьте следующие строки в UrlController под конструктором, чтобы определить обработчик маршрута POST:

...
  @Post('shorten')
  shortenUrl(
    @Body()
    url: ShortenURLDto,
  ) {
    return this.service.shortenUrl(url);
  }

Вы создаете метод shortenUrl, который принимает аргумент url с типом ShortenURLDto. Вы аннотируете url с помощью декоратора Body, чтобы извлечь объект body из объекта запроса и заполнить переменную url его значением.

Затем вы аннотируете весь метод декоратором Post и передаете shorten в качестве аргумента. Этот обработчик будет обрабатывать все запросы Post, сделанные на localhost:/shorten. Затем вы вызываете метод shortenUrl для service и передаете url в качестве аргумента.

Затем добавьте следующие строки под маршрутом POST, чтобы определить обработчик маршрута GET для перенаправления:

...
  @Get(':code')
  async redirect(
    @Res() res,
    @Param('code')
    code: string,
  ) {
    const url = await this.service.redirect(code);

    return res.redirect(url.longUrl);
  }

Вы создаете метод redirect с двумя параметрами: res с аннотацией декоратора Res и code с аннотацией декоратор Param. Декоратор Res превращает класс, который он аннотирует, в объект экспресс-ответа, позволяя вам использовать специфичные для библиотеки команды, такие как экспресс-метод redirect. Декоратор Param извлекает свойство params из объекта req и заполняет оформленный параметр его значением.

Вы аннотируете метод redirect с помощью декоратора Get и передаете параметр подстановки, :code. Затем вы передаете code в качестве аргумента декоратору Param.

Затем вы вызываете метод redirect для service, ожидаете результата и сохраняете его в переменной url.

Наконец, вы возвращаете res.redirect() и передаете url.longUrl в качестве аргумента. Этот метод будет обрабатывать запросы GET к localhost:/code или вашим сокращенным URL-адресам.

Ваш файл src/url/url.controller.ts теперь будет выглядеть так:

import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
import { UrlService } from './url.service';
import { ShortenURLDto } from './dtos/url.dto';

@Controller()
export class UrlController {
  constructor(private service: UrlService) {}

  @Post('shorten')
  shortenUrl(
    @Body()
    url: ShortenURLDto,
  ) {
    return this.service.shortenUrl(url);
  }

  @Get(':code')
  async redirect(
    @Res() res,
    @Param('code')
    code: string,
  ) {
    const url = await this.service.redirect(code);

    return res.redirect(url.longUrl);
  }
}

Сохраните и закройте файл.

Теперь, когда вы определили обработчики маршрутов POST и GET, ваш сокращатель URL-адресов полностью функционален. На следующем шаге вы проверите его.

Шаг 5 — Тестирование сокращателя URL

На этом шаге вы протестируете средство сокращения URL-адресов, которое вы определили на предыдущих шагах.

Сначала запустите приложение, выполнив:

  1. npm run start

Вы увидите следующий вывод:

Output
[Nest] 12640 - 06/08/2022, 16:20:04 LOG [NestFactory] Starting Nest application... [Nest] 12640 - 06/08/2022, 16:20:07 LOG [InstanceLoader] AppModule dependencies initialized +2942ms [Nest] 12640 - 06/08/2022, 16:20:07 LOG [InstanceLoader] TypeOrmModule dependencies initialized +1ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +257ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] TypeOrmModule dependencies initialized +2ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] UrlModule dependencies initialized +4ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RoutesResolver] UrlController {/}: +68ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RouterExplorer] Mapped {/shorten, POST} route +7ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RouterExplorer] Mapped {/:code, GET} route +2ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [NestApplication] Nest application successfully started +7ms

Откройте новый терминал, чтобы использовать curl или предпочитаемый вами инструмент тестирования API, чтобы отправить запрос POST на http://localhost:3000/shorten с помощью данные ниже или любые данные по вашему выбору. Дополнительные сведения об использовании curl см. в описании этого руководства.

Запустите эту команду, чтобы сделать пример запроса POST:

  1. curl -d "{\"longUrl\":\"http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk\"}" -H "Content-Type: application/json" http://localhost:3000/shorten

Флаг -d регистрирует данные запроса HTTP POST, а флаг -H устанавливает заголовки для HTTP. запрос. Выполнение этой команды отправит длинный URL-адрес вашему приложению и вернет сокращенный URL-адрес.

В ответ вы получите короткий URL-адрес, как в следующем примере:

http://localhost:3000/MWBNHDiloW

Наконец, скопируйте короткий URL-адрес и вставьте ссылку в браузер. Затем нажмите ENTER. Вы будете перенаправлены на исходный ресурс.

Заключение

В этой статье вы создали средство сокращения URL-адресов с помощью NestJS. Если вы добавите Github.

NestJS обеспечивает безопасность типов и архитектуру, чтобы сделать ваше приложение более безопасным, удобным в сопровождении и масштабируемым. Чтобы узнать больше, посетите официальную документацию NestJS.