Как создать безопасный сокращатель 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:
- npm install -g @nestjs/cli
Флаг -g
глобально установит Nest CLI в вашей системе.
Вы увидите следующий вывод:
- Output...
- added 249 packages, and audited 250 packages in 3m
- 39 packages are looking for funding
- run npm fund for details
- found 0 vulnerabilities
Затем вы будете использовать команду new
для создания проекта и создания необходимых шаблонных начальных файлов:
- nest new URL-shortener
Вы увидите следующий вывод:
- Output...
- ⚡ We will scaffold your app in a few seconds..
-
- CREATE url-shortener/.eslintrc.js (631 bytes)
- CREATE url-shortener/.prettierrc (51 bytes)
- CREATE url-shortener/nest-cli.json (118 bytes)
- CREATE url-shortener/package.json (2002 bytes)
- CREATE url-shortener/README.md (3339 bytes)
- CREATE url-shortener/tsconfig.build.json (97 bytes)
- CREATE url-shortener/tsconfig.json (546 bytes)
- CREATE url-shortener/src/app.controller.spec.ts (617 bytes)
- CREATE url-shortener/src/app.controller.ts (274 bytes)
- CREATE url-shortener/src/app.module.ts (249 bytes)
- CREATE url-shortener/src/app.service.ts (142 bytes)
- CREATE url-shortener/src/main.ts (208 bytes)
- CREATE url-shortener/test/app.e2e-spec.ts (630 bytes)
- CREATE url-shortener/test/jest-e2e.json (183 bytes)
-
- ? Which package manager would you ❤️ to use? (Use arrow keys)
- > npm
- yarn
- pnpm
Выберите npm
.
Вы увидите следующий вывод:
- Output√ Installation in progress... ☕
-
- 🚀 Successfully created project url-shortener
- 👉 Get started with the following commands:
-
- $ cd url-shortener
- $ npm run start
-
- Thanks for installing Nest 🙏
- Please consider donating to our open collective
- to help us maintain this package.
-
- 🍷 Donate: https://opencollective.com/nest
Перейдите в созданный каталог проекта:
- 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:
- npm install @nestjs/typeorm typeorm
SQLite — это библиотека, которая реализует небольшой, быстрый, автономный механизм базы данных SQL. Вы будете использовать эту зависимость в качестве базы данных для хранения и извлечения сокращенных URL-адресов.
Выполните следующую команду, чтобы установить SQLite:
- npm install sqlite3
Пакет class-validator
содержит декораторы, используемые для проверки данных в NestJS. Вы будете использовать эту зависимость с вашим объектом передачи данных для проверки данных, отправленных в ваше приложение.
Выполните следующую команду, чтобы установить class-validator
:
- npm install class-validator
Пакет class-transformer
позволяет преобразовывать обычные объекты в экземпляры класса и наоборот. Вы будете использовать эту зависимость с class-validator
, так как она не может работать одна.
Выполните следующую команду, чтобы установить class-transformer
:
- npm install class-transformer
Nano-ID — это безопасный генератор уникальных строковых идентификаторов с поддержкой URL-адресов. Вы будете использовать эту зависимость для создания уникального идентификатора для каждого ресурса URL.
Выполните следующую команду, чтобы установить Nano-ID:
- npm install nanoid@^3.0.0
Примечание. Версии Nano-ID выше 3.0.0
отключили поддержку модулей CommonJS. Эта проблема может вызвать ошибку в вашем приложении, поскольку код JavaScript, созданный компилятором TypeScript, по-прежнему использует систему модулей CommonJS.
После того, как вы установили необходимые зависимости, вы сгенерируете модуль, службу и контроллер проекта с помощью интерфейса командной строки Nest. Модуль будет организовывать ваш проект, сервис будет обрабатывать всю логику для сокращения URL-адресов, а контроллер будет обрабатывать маршруты.
Запустите следующую команду, чтобы сгенерировать модуль:
- nest generate module url
Вы увидите следующий вывод:
OutputCREATE src/url/url.module.ts (80 bytes)
UPDATE src/app.module.ts (304 bytes)
Затем выполните следующую команду для создания службы:
- nest generate service url --no-spec
Флаг --no-spec
указывает интерфейсу командной строки Nest создавать файлы без их тестовых файлов. В этом руководстве вам не понадобятся тестовые файлы.
Вы увидите следующий вывод:
OutputCREATE src/url/url.service.ts (87 bytes)
UPDATE src/url/url.module.ts (151 bytes)
Затем выполните следующую команду, чтобы сгенерировать контроллер:
- nest generate controller url --no-spec
Вы увидите следующий вывод:
OutputCREATE src/url/url.controller.ts (95 bytes)
UPDATE src/url/url.module.ts (233 bytes)
На этом шаге вы создали свое приложение и большинство файлов, необходимых для разработки. Далее вы подключите свое приложение к базе данных.
Шаг 2 — Подключение вашего приложения к базе данных
На этом шаге вы создадите объект для моделирования ресурса URL в вашей базе данных. сущность — это файл, содержащий необходимые свойства хранимых данных. Вы также создадите репозиторий в качестве уровня доступа между вашим приложением и его базой данных.
Используя nano
или предпочитаемый вами текстовый редактор, создайте и откройте в папке src/url
файл с именем url.entity.ts
:
- 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
или предпочитаемом вами текстовом редакторе:
- 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
:
- 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
:
- 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
:
- mkdir src/url/dtos
Затем создайте файл с именем url.dto.ts
в этой папке:
- 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
:
- 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
:
- 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
:
- 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:
. Затем вы вызываете метод 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:
или вашим сокращенным 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-адресов, которое вы определили на предыдущих шагах.
Сначала запустите приложение, выполнив:
- 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
:
- 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.