想要吸引招聘人员的注意?只需⌚ 5 分钟即可构建这个🔥项目,🚀 丰富你的作品集!
入门🚀
依赖项⚠️
文件夹设置🎪
服务器设置🌀!
创建路由器⚡!
创建控制器⚡!
数据模型❄️!
前端👀!
摘要🙏
准备好创建作品集却苦于没有灵感了吗?这里有一个绝佳的创意,可以展示你的全栈技能,给潜在雇主留下深刻印象!💥
入门🚀
mkdir url-shortener
cd url-shortener
npm init -y
在这里,我们创建一个目录来存储我们的项目,并用npm初始化它。
依赖项⚠️
npm install dotenv express mongoose nanoid
我们安装了一些将在整个项目中使用的依赖项:
- dotenv(利用环境变量的库)
- express(Express.js 用于创建我们的服务器应用程序)
- mongoose(ODM 将我们的 URL 存储在我们的 MongoDB 数据库中)
文件夹设置🎪
我们需要确保我们的项目看起来像这样:
url-shortener/
├── package.json
├── client
│ ├── app.js
│ ├── index.html
│ └── style.css
└── server
├── controllers
│ └── url.controller.js
├── index.js
├── models
│ └── url.model.js
└── routes
└── url.routes.js
我们将代码拆分为路由、控制器和模型。通过关注点分离,使代码更易于维护!
服务器设置🌀!
在我们的文件中server/index.js
,添加以下内容:
const express = require('express');
const mongoose = require('mongoose');
require('dotenv').config();
const urlRouter = require('./routes/url.routes');
const PORT = process.env.PORT || 8080;
const DB_URL = process.env.DB_URL || 'mongodb://localhost:27017/db';
const db = mongoose.connect(DB_URL, {
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true
}
).
then(res => res)
.catch(err => console.log(err));
const app = express();
app.use(express.json());
app.use(express.static('client'));
app.use('/url', urlRouter);
app.listen(PORT, () => {
console.log(`Server listening at http://localhost:${PORT}`);
});
在这里,我们导入express和mongoose。
然后导入即将创建的路由器来处理我们的URL。
然后初始化我们的数据库连接来存储我们的数据。
接下来我们创建我们的express 应用程序并使用我们的中间件(express.json()、express.static() 和我们的路由器)
创建路由器⚡!
在我们的文件中server/routes/url.routes.js
,添加以下内容:
const express = require('express');
const urlRoutes = express.Router();
const controller = require('../controllers/url.controller');
urlRoutes.get('/:slug',
controller.getUrl);
urlRoutes.post('/new',
controller.postUrl);
module.exports = urlRoutes;
在这里,我们导入express并创建一个express 路由器来连接我们的路由。
然后,我们导入控制器来处理调用的请求。
最后,我们创建GET和POST请求来处理检索和创建缩短的URL
创建控制器⚡!
现在我们需要一个控制器来处理这些路线!
在我们的文件中server/controllers/url.controller.js
,添加以下内容:
const UrlModel = require('../models/url.model');
const {nanoid} = require('nanoid');
exports.getUrl = async (req, res) => {
const {slug} = req.params;
// check if slug exists
const foundSlug = await UrlModel.findOne({slug});
// if no slug exists, create one
if(!foundSlug || foundSlug.length == 0) {
let fullUrl = req.protocol + '://' + req.get('Host') + req.originalUrl;
res.status(404).json({message: "URL not found.", body:{slug, url: fullUrl}});
} else {
res.status(302).redirect(foundSlug.url);
}
}
exports.postUrl = async (req, res) => {
let {url, slug} = req.body;
// check if slug provided, create new one if not.
if(!slug) {
slug = nanoid(5);
}
slug = slug.toLocaleLowerCase();
// check if slug exists
const foundSlug = await UrlModel.find({slug});
// if no slug exists, create one
if(!foundSlug || foundSlug.length == 0) {
const newUrl = new UrlModel(
{
slug,
url
}
);
const response = await newUrl.save();
res.status(200).json({message: "Creation successful!", body:response});
} else {
res.status(409).json({message: "Resource already exists.", body:{slug: "", url:""}});
}
}
这就是我们使用依赖项nanoid 的地方。
什么是纳米粒子?
nanoid是一个用于生成短 ID 字符串的库。我们将生成一个短 ID 字符串作为我们的短 URL!
GET请求
GET请求从get url中检索slug 值并尝试从数据库中检索匹配的条目。:slug
如果找到匹配的 slug,那么我们将重定向到找到的 slug 的URL 。
如果没有找到 slug,我们会以 404 状态通知用户未找到所需的 URL。
POST请求
POST请求从POST请求正文中检索url 和 slug,如果没有提供 slug,我们将使用nanoid生成长度为 5 的随机slug 。
这样用户就可以创建自定义短 URL。
示例请求:
POST http://localhost:8080/url/new HTTP/1.1
content-type: application/json
{
"slug": "abcde",
"url": "https://www.google.com"
}
这将创建一个 URL,http://localhost:8080/abcde
将用户重定向到https://www.google.com
我们检查数据库中是否已经存在具有所需 slug 的条目。
如果不存在条目,我们将新文档保存到数据库并返回创建的条目。
如果 slug 存在,我们将返回 409 响应,通知用户资源已存在。
数据模型❄️!
为我们的后端构建的最后一件事是 mongoose 将用于我们的 MongoDB 数据库的数据模型。
在我们的文件中server/models/url.model.js
,添加以下内容:
const mongoose = require('mongoose');
const UrlModel = mongoose.model('Url',
mongoose.Schema(
{
slug: {
type: String,
minlength: [5, 'Slug does not contain enough characters (Minimum 5).'],
maxlength: [5, 'Slug contains too many characters (Maximum 5).'],
trim: true,
validate: {
validator : (slug) => {
return /[\w\-]/.test(slug);
},
message: props => `${props.value} is not a valid slug.`
}
},
url: {
type: String,
required: [true, 'A valid URL must be provided.'],
trim: true
}
},
{timestamps: true}
)
);
module.exports = UrlModel;
在这个脚本中,我们首先导入 mongoose来创建我们的mongoose 模型。
然后我们创建一个具有两个参数的UrlModel Schema:
- slug(缩短的 URL 的字符串值)
- url(要重定向到的 URL 的字符串值)
我们使用正则表达式为 slug创建一些基本验证,以确保 slug 仅包含字母数字字符和连字符(-)。
后端就完成了👏!是时候构建我们的前端了!
前端👀!
我们的客户端目录应包含以下文件:
client/
├── app.js
├── index.html
└── style.css
index.html 文件
在我们的文件中index.html
,添加以下表单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>MicroUrl</title>
</head>
<body>
<main>
<h1>MicroUrl</h1>
</main>
<section>
<form action="javascript:;" onsubmit="createUrl(displayResponse)">
<label for="url">Url to shorten:</label>
<input type="url" name="url" id="url" required>
<label for="slug">Optional. Custom micro url:</label>
<input type="text" name="slug" id="slug">
<input type="submit" value="Create">
</form>
</section>
<section id="response">
</section>
<script src="app.js"></script>
</body>
</html>
我们的表单包含两个输入(一个用于缩短URL ,另一个用于潜在的自定义 slug)
style.css 文件
在我们的文件中style.css
,添加以下表单:
body {
margin-top: 20vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #84613D;
font-family: "Lucida Console", Monaco, monospace;
background: #FDF9EA;
}
body > * {
width: 40vw;
height: auto;
}
form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: stretch;
margin: 1rem 0;
}
form > * {
margin: .5rem 0;
padding: 1rem;
}
form > button {
padding: 0;
}
最后要做的是添加 Javascript 来创建我们的 URL 并显示响应!
app.js 文件
在我们的文件中app.js
,添加以下表单:
const createUrl = async (callback=null) => {
this.preventDefault;
let response = await fetch('/url/new', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(
{
url:this.url.value,
slug:this.slug.value
})
});
let result = await response.json();
console.log(result);
if(callback) {
callback("response", result);
}
}
const displayResponse = (elementKey, data) => {
const {message, body} = data;
const parentElement = document.getElementById(elementKey);
parentElement.innerHTML = "";
let divElement = document.createElement('div');
let pElement = document.createElement('p');
pElement.appendChild(document.createTextNode(message));
let aElement = document.createElement('a');
if(body.slug) {
aElement.appendChild(document.createTextNode(`${window.location.href}url/${body.slug}`));
aElement.href = `${window.location.href}url/${body.slug}`;
} else {
aElement.appendChild(document.createTextNode(""));
}
divElement.appendChild(pElement);
divElement.appendChild(aElement);
parentElement.appendChild(divElement);
}
我们有两个功能:
- 创建Url
- 显示响应
createUrl
接受回调作为参数,在处理完此表单的提交后执行。
这可以称为callback design pattern
createUrl函数用于将表单数据POST到服务器。完成后,我们使用displayResponse函数显示新创建的短 URL:fetch
摘要🙏
如果你已经完成了这一步,恭喜你!🎉
你在这个项目中学到了很多东西。API 创建、数据验证、前端设计。现在你应该已经准备好创建一个🔥作品集了!
如果您喜欢本教程,请随时关注我并查看我的一些社交媒体!
Twitter
Github