應(yīng)用場(chǎng)景:發(fā)短信的時(shí)候,可能由于參數(shù)的問(wèn)題導(dǎo)致鏈接過(guò)長(zhǎng),超出短信字?jǐn)?shù)限制,所以鏈接需要越短越好
原理
生成一個(gè)唯一的標(biāo)志,來(lái)對(duì)鏈接進(jìn)行標(biāo)記,存入數(shù)據(jù)庫(kù)。如
{
fullUrl: 'https://www.baidu.com',
shortUrl: 'jgXqZSu8W'
}
也就是說(shuō),根據(jù) shortUrl 的值,找到它的 fullUrl,然后重定向跳轉(zhuǎn)。
實(shí)現(xiàn)
1. Express 創(chuàng)建項(xiàng)目
- 新建文件夾
mkdir shortUrl - 初始化項(xiàng)目
npm init -y - 安裝依賴包,請(qǐng)參照
package.json,npm i -S express - 在根目錄新建
app.js文件 - 修改
package.json的運(yùn)行命令,在 script 中添加"start": "node app.js"; - 編寫
app.js
const express = require('express');
const app = express();
app.get('/', async (req, res) => {
res.send('hello world')
});
app.listen(5000)
- 運(yùn)行
npm start, 打開頁(yè)面locahost:5000,可以看到 hello world 表示項(xiàng)目運(yùn)行成功。大框架已經(jīng)有了,接下來(lái)需要進(jìn)行修修改改。
2. 創(chuàng)建數(shù)據(jù)庫(kù)
數(shù)據(jù)庫(kù)的操作大同小異,這邊以 sqlite3 為例。因?yàn)椴恍枰诒镜匕惭b客戶端即可使用,比較方便。為了簡(jiǎn)化操作,另外安裝了 sequelize進(jìn)行數(shù)據(jù)庫(kù)操作。
- 在根目錄新建文件夾
db, 在該文件夾里新建文件index.js,shortUrl.js - 安裝依賴
npm i -S shordid sqlite3 sequelize
首先,讓我們編寫shortUrl.js,
// shortUrl.js
const shortId = require('shortid'); // 用于生成短鏈接的碼
module.exports = (sequelize, DataTypes) => {
const ShortUrlModel = sequelize.define('ShortUrlModel', {
full: {
type: DataTypes.STRING
},
short: {
type: DataTypes.STRING,
defaultValue: shortId.generate
},
})
ShortUrlModel.associate = function (models) {
}
return ShortUrlModel
}
// config.js
const path = require('path');
module.exports = {
db: {
database: process.env.DB_NAME || 'shorturl',
user: process.env.DB_USER || 'shorturl',
password: process.env.DB_PASS || 'shorturl',
options: {
dialect: process.env.DIALECT || 'sqlite',
host: process.env.HOST || 'localhost',
storage: path.resolve(__dirname, './shorturl.sqlite')
}
}
}
然后,編寫 index.js
// index.js
const {Sequelize} = require('sequelize');
const config = require('../config.js');
const fs = require('fs');
const path = require('path');
const db = {};
const sequelize = new Sequelize(
config.db.database,
config.db.user,
config.db.password,
config.db.options
);
// 將文件夾下的數(shù)據(jù)模型導(dǎo)入,這樣就不用手動(dòng)一個(gè)個(gè)寫了
fs
.readdirSync(__dirname)
.filter((file) =>
file !== 'index.js'
)
.forEach((file) => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes)
db[model.name] = model
})
// 抄過(guò)來(lái)的,我也不知道是啥意思,等知道了再補(bǔ)充
Object.keys(db).forEach(function (modelName) {
console.log(Object.keys(db))
if ('associate' in db[modelName]) {
db[modelName].associate(db)
}
})
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
3. 改寫 app.js
const express = require('express');
const app = express();
const {sequelize, ShortUrlModel} = require('./db');
// sequelize連接數(shù)據(jù)庫(kù)
sequelize.sync({force: true}) // force: true, 每次重啟項(xiàng)目初始化的時(shí)候,會(huì)清空數(shù)據(jù)
.then(() => {
console.log(`Data base created`)
})
// 設(shè)置模版引擎
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: false })); // 用于獲取request的參數(shù)
app.get('/', async (req, res) => {
// 獲取所有的鏈接,來(lái)進(jìn)行渲染
const shortUrls = await ShortUrlModel.findAll();
res.render('index', {shortUrls});
});
app.post('/shortUrls', async (req, res) => {
// 提交鏈接數(shù)據(jù),并保存
await ShortUrlModel.create(req.body);
res.redirect('/');
})
app.get('/:short', async (req, res) => {
// 根據(jù)短鏈接碼找到這條數(shù)據(jù),數(shù)據(jù)里頭有完整鏈接
const shortUrl = await ShortUrlModel.findOne({
where: {
short: req.params.short
}
});
// 得到完整鏈接后,進(jìn)行重定向
res.redirect(shortUrl.full)
})
app.listen(5000)
4. 編寫 view/index.ejs
<!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" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title>Document</title>
</head>
<body>
<div class="container">
<h1>URL Shrinker</h1>
<form action="/shortUrls" method="POST" class="my-4 form-inline">
<label for="full" class="sr-only">Url</label>
<input required placeholder="Url" type="url" name="full" id="full" class="form-control col mr-2">
<button class="btn btn-success" type="submit">Shrink</button>
</form>
<table class="table table-striped table-responsive">
<thead>
<tr>
<th>Full URL</th>
<th>Short URL</th>
</tr>
</thead>
<tbody>
<% shortUrls.forEach(shortUrl => { %>
<tr>
<td><a href="<%= shortUrl.full %>"><%= shortUrl.full %></a></td>
<td><a href="<%= shortUrl.short %>"><%= shortUrl.short %></a></td>
</tr>
<% }) %>
</tbody>
</table>
</div>
</body>
</html>