使用 NodeJS 和 MongoDB 构建 URL 缩短服务。部署到 Azure。
前端
服务器
耶,我们已经走了很长一段路了!!
下一步
最后一步!!!
大家好,我们将使用 NodeJS、Express 和 MongoDB 构建一个 URL 缩短服务。然后,我们将把我们的 Web 应用程序部署到 Azure。这将是一个完整的代码教程,我会详细解释每一行代码。
最后将添加演示和 GitHub 存储库的链接。
我应该知道/拥有什么
- 对 HTML、CSS 和 Javascript 有基本的了解
- 在您的计算机上安装 NodeJS(在此处安装)
- 在您的计算机上安装 MongoDB(在此处安装)
- 体验创建 GitHub 存储库,并将本地存储库推送到远程存储库。
让我们开始吧
首先,让我们为我们的应用程序创建一个文件夹。我们将其命名为 url-shortener。
然后在终端运行npm init。 这将为我们创建一个 package.json 文件。
现在让我们安装我们将要使用的软件包。express :Node.js框架,为 Web 和移动应用程序提供了一组强大的功能。
body-parser:在处理程序之前解析传入的请求主体。
mongoose:Mongoose 是一个 MongoDB 对象建模工具,旨在在异步环境中工作。
nodemon:用于自动重启服务器,这样我们就不必在每次更改时都停止并重启服务器。我们将其安装为开发依赖项,因为我们只在开发过程中需要它。
安装完成后,编辑 package.json 的主要内容和脚本,如下所示。
{
"name" : "url-shortener",
"version" : "1.0.0",
"description" : "URL shotener web app",
"main" : "server.js",
"scripts" : {
"dev" : "nodemon server.js",
"start" : "node server.js"
},
"keywords" : ["URL", "shortener"],
"author" : "Your name",
"dependencies" : {
"express" : "^4.17.1",
"mongoose" : "^5.9.7",
"body-parser" : "^1.19.0"
},
"devDependencies" : {
"nodemon" : "^2.0.2"
}
}
前端
我们将使用一个非常基础的 UI。
对于应用程序的前端,请在工作目录中创建一个名为 public 的文件夹。我们将在这里存放前端文件(HTML、CSS 和 JavaScript)。在 public 文件夹中创建名为 index.html、style.css 和 main.js 的文件。index.html 和 style.css 的内容如下所示:
索引.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width,
initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="style.css">
<title>URL shortener</title>
</head>
<body>
<form id="url-form">
<h1 class="header">URL SHORTENER</h1>
<p class="desc">Shorten your long URL to
<span class="domain">mydomain.com</span>/unique_name
</p>
<p>
<input required class="url-input"
id="original-url" type="url"
placeholder="paste original URL here">
</p>
<input disabled class="base-url" value="">
<input required class="unique-input" id="unique-name"
type="text" placeholder="unique name">
<p id='status'><button>SHORTEN</button></p>
<p id="confirmationShow"></p>
</form>
</body>
<script>
const domain = window.location.host;
document.querySelector('.domain').innerText = domain;
document.querySelector('.base-url').value = domain;
</script>
<script src="main.js"></script>
</html>
样式.css:
body{
text-align: center;
display: flex;
flex-direction: column;
justify-content: space-between;
background : linear-gradient(to right, #aa5f15, #542008);
}
html, body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
overflow: hidden;
height: 100%;
}
form{
border: red;
padding-top: 15vh
}
.a {
color : white;
}
.header{
color: bisque;
letter-spacing: 3px;
font-size: 3rem;
margin-bottom: 1px;
}
.header span {
font-style: italic;
}
.desc{
margin-top :2px;
color: bisque;
}
.base-url{
padding: 10px;
background-color: #a7a7a7;
border-radius: 8px 0 0 8px;
border: 1px solid black;
width: 100px;
font-weight: bold
}
.unique-input{
padding: 10px;
border-radius: 0 8px 8px 0;
outline: none;
border: 1px solid black;
}
.url-input{
border-radius: 8px;
padding: 10px;
width: 300px;
outline : none;
}
button{
background-color: burlywood;
padding: 10px;
border-radius: 10px;
outline: none;
cursor: pointer;
}
#confirmationShow {
font-style: italics;
}
.loader {
border: 8px solid #f3f3f3;
border-radius: 50%;
border-top: 8px solid orange;
width: 10px;
height: 10px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
margin: 8px auto !important;
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
服务器
在根目录下创建文件 server.js,并添加以下内容
服务器.js:
//Import modules
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
//Call the express function to initiate an express app
const app = express();
//This tells express to parse incoming requests
app.use(bodyParser.json());
//This tells express we are serving static files (front end files)
app.use(express.static(path.join(__dirname, 'public')));
/** NB: process.env.PORT is required as you would
not be able to set the port manually in production */
const PORT = process.env.PORT || 3000;
//app to listen to specified port
app.listen(PORT, () => {
console.log(`Server running on port${PORT}`);
});
请注意,路径是内置节点模块,不需要安装
打开浏览器并访问http://localhost:3000。应该会显示出来。
耶,我们的公共页面正在提供服务。
现在进入下一部分
让我们连接到我们的 MongoDB
在根目录中创建一个名为db.js的文件,并将其放入其中。
db.js:
//import mongoose library
const mongoose = require('mongoose');
//MONGO_URI
const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/url';
//Connect to DB function
const connect = () => {
mongoose.connect(MONGO_URI, {useNewUrlParser : true, useUnifiedTopology : true})
.then(() => console.log('DB connected'))
.catch(err => console.log(err));
//On connection error, log the message
mongoose.connection.on('error', err => {
console.log(`DB connection error : ${err.message}`);
});
}
//export the connect function, to use in server.js
module.exports = { connect };
现在让我们回到我们的server.js,并实现与数据库的连接功能
服务器.js:
//Import modules
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
//Import db module
const db = require('./db.js');
//Call the express function to initiate an express app
const app = express();
//Connect to database by calling our connect method
db.connect();
//This tells express to parse incoming requests
app.use(bodyParser.json());
//This tells express we are serving static files (front end files)
app.use(express.static(path.join(__dirname, 'public')));
/** NB: process.env.PORT is required as you would
not be able to set the port manually in production */
const PORT = process.env.PORT || 3000;
//app to listen to specified port
app.listen(PORT, () => {
console.log(`Server running on port${PORT}`);
});
确保本地 Mongo 服务器正在运行。
服务器重启后,你应该会在终端中看到以下信息:
创建 URL 模型
现在我们已经成功连接到数据库,让我们创建 URL 模型,它将保存我们想要在数据库中存储 URL 的格式。
创建一个名为 url.model.js 的文件,并将其放入。
url.model.js :
const mongoose = require('mongoose');
//create Url Schema (format)
const urlSchema = new mongoose.Schema({
originalUrl: {
type : String,
required : true
},
shortUrl : {
type : String,
required : true
},
unique_name : {
type : String,
required : true
},
dateCreated : {
type : Date,
default : Date.now
}
});
//Use schema to create a Url model
const Url = mongoose.model('Url', urlSchema);
//Export Url Model
module.exports = Url;
创建控制器来处理所有路由
我们现在将创建控制器来处理我们的两个路线:
- 创建短链接
- openShortLink 创建一个名为 url.controllers.js 的文件并添加以下代码:
url.controller.js :
//import Url model
const Url = require('./url.model.js');
//This is basically your domain name
const baseUrl = process.env.BASE_URL || 'http://localhost:3000';
const createShortLink = async (req, res) => {
//get the originalUrl and unique_name from the request's body
let { originalUrl, unique_name } = req.body;
try {
//check if unique_name alredy exists
let nameExists = await Url.findOne({ unique_name });
/** if unique_name already exists, send a response with an
error message, else save the new unique_name and originalUrl */
if(nameExists){
return res.status(403).json({
error: "Unique name already exists, choose another",
ok : false
})
}
else {
const shortUrl = baseUrl + '/' + unique_name;
url = new Url({
originalUrl,
shortUrl,
unique_name
});
//save
const saved = await url.save();
//return success message shortUrl
return res.json({
message : 'success',
ok : true,
shortUrl
});
}
} catch (error) {
///catch any error, and return server error
return res.status(500).json({ok : false, error : 'Server error'});
}
};
const openShortLink = async (req, res) => {
//get the unique name from the req params (e.g olamide from shorten.me/olamide)
const { unique_name } = req.params;
try{
//find the Url model that has that unique_name
let url = await Url.findOne({ unique_name });
/** if such Url exists, redirect the user to the originalUrl
of that Url Model, else send a 404 Not Found Response */
if(url){
return res.redirect(url.originalUrl);
} else {
return res.status(404).json({error : 'Not found'});
}
} catch(err) {
//catch any error, and return server error to user
console.log(err);
res.status(500).json({error : 'Server error'});
}
};
module.exports = {
createShortLink, openShortLink
}
配置路线
让我们回到 server.js 并在路由中使用刚刚创建的控制器。
首先,我们需要导入它们并按如下所示使用。
服务器.js:
//Import modules
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
//Import db module
const db = require('./db.js');
//Import controllers
const { createShortLink, openShortLink } = require('./url.controller.js');
//Call the express function to initiate an express app
const app = express();
//Connect to database by calling our connect method
db.connect();
//This tells express to parse incoming requests
app.use(bodyParser.json());
//This tells express we are serving static files (front end files)
app.use(express.static(path.join(__dirname, 'public')));
//USE CONTROLLERS
//route to create short link
app.post('/createShortLink', createShortLink);
//route to open short link, ':' means unique_name is a param
app.get('/:unique_name', openShortLink);
/** NB: process.env.PORT is required as you would
not be able to set the port manually in production */
const PORT = process.env.PORT || 3000;
//app to listen to specified port
app.listen(PORT, () => {
console.log(`Server running on port${PORT}`);
});
耶,我们已经走了很长一段路了!!
现在让我们开始从前端发出请求。
打开 public/main.js 文件并添加以下内容:
main.js :
const urlForm = document.getElementById('url-form');
const originalUrl = document.getElementById('original-url');
const uniqueName = document.getElementById('unique-name');
const confirmationShow = document.getElementById('confirmationShow');
const status = document.getElementById('status');
const formSubmit = e => {
e.preventDefault();
status.innerHTML = '<button type="button" class="loader"></button>'
fetch('/createShortLink', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
originalUrl : originalUrl.value,
unique_name : uniqueName.value
})
})
.then(data => data.json())
.then(response => {
status.innerHTML = '<button>SHORTEN</button>'
if(!response.ok){
confirmationShow.innerText = response.error;
}
else {
confirmationShow.innerHTML = `Hooray!!! The link can now be visited
through <a target="_blank"
href=${response.shortUrl} rel = "noopener noreferer" >
${response.shortUrl} </a>`;
}
})
.catch(err => {
console.log('oops', err);
status.innerHTML = '<button>SHORTEN</button>';
confirmationShow.innerText = 'Network error, retry'
})
};
urlForm.addEventListener('submit', formSubmit);
就是这样 !!!
现在确保你的服务器正在运行,打开浏览器,访问http://localhost:3000。在原始 URL 字段中输入一个长 URL,并在唯一名称字段中输入一个唯一名称。提交表单,看看奇迹是否发生了。
下一步
GitHub 存储库
为项目创建一个 GitHub 存储库,并将项目推送到远程存储库(按照本指南操作)
MongoDB 服务器
在将项目部署到 Azure 之前,我们需要一个远程 MongoDB 服务器,因为 Azure 无法连接到我们本地服务器上的数据库。前往MongoDB Atlas获取连接字符串。这将是服务器上的 MONGO_URI 变量。(还记得我们之前在应用中添加了 process.env.MONGO_URI 吗?)您可以按照本指南获取连接字符串。
最后一步!!!
部署到 Azure
就这样!我们的应用上线啦!!!
转到您的网站的 URL 并进行测试。
按照本指南为该应用购买并设置一个实际的短自定义域名。我购买的是rdre.me。
您可以继续向您的应用添加更多功能,例如在用户创建短链接之前对其进行注册、设置短链接到期日期等。
感谢您来到这里。
演示链接:https://rdre.me
GitHub 存储库链接:https://github.com/aolamide/shorten-url
请留下您的评论和问题。
文章来源:https://dev.to/olamideaboyeji/building-a-url-shortening-service-with-nodejs-and-mongobb-deploy-to-azure-oep