Connecting Your Application to MongoDB
01 Connect to MongoDB and Use MongoDB Compass GUI
npm install mongoose --save
控制台下运行:mongo, 进入mongodb控制台
- 修改dev-server/api/config/env.js
function setDevEnv(app) {
// console.log("setting development environment");
process.env.NODE_ENV='development environment';
process.env.DB_URL='mongodb://localhost:27017/mevn-dev-db';
app.use(bodyParser.json());
app.use(morgan('dev'));
app.use(cors());
}
function setProdEnv(app) {
process.env.DB_URL='mongodb://localhost:27017/mevn-prod-db';
app.use(bodyParser.json());
app.use(express.static(__dirname + '/../dist'));
// console.log("setting production environment");
}
- 修改dev-server/api/config/db.js
import mongoose from "mongoose";
export function connectToDB() {
mongoose.connect(process.env.DB_URL, { useNewUrlParser: true }, (error) => {
if (error) {
console.log("Unable to connect to database");
throw error;
} else {
console.log("Connected to MongoDB");
}
});
}
- 修改dev-server/index.js,引入connetToDB
// const express = require('express')
import express from "express";
const port = 3000;
const app = express();
import { registerRoutes } from "./routes";
import { setEnvironment } from "./config/env";
import { connectToDB } from "./config/db";
setEnvironment(app);
connectToDB();
registerRoutes(app);
// app.get('/', (req, res) => res.send('Hello World!'))
app.get("/", (req, res) => {
if (process.env.NODE_ENV !== "production") {
return res.send("Running server in devlopment mode.");
} else {
return res.sendFile("index.html"), { root: __dirname + "/../dist" };
}
});
app.listen(port, () => {
const msg = `MEVN app listening on port ${port} and run in ${process.env.NODE_ENV} mode!`;
console.log(msg);
});
02 Working with Entity Models
参考:http://mongoosejs.com/docs/guide.html 创建model,相当于关系型数据库创建表。
- 新建dev-server/model/task-model.js
import mongoose from "mongoose";
const taskSchema = new mongoose.Schema({
title: String,
body: String,
dueDate: { type: Date, default: Date.now },
completed: { type: Boolean, default: false },
author: { type: mongoose.Schema.Types.ObjectId, ref: "user" },
});
taskSchema.set("timestamps", true);
export default mongoose.model("task", taskSchema);
- 新建dev-server/model/user-model.js
import mongoose from "mongoose";
import { StringUtil } from "../utilities/string-util";
const userSchema = new mongoose.Schema({
username: String,
first: String,
last: String,
password: String,
});
userSchema.set("timestamps", true);
// 虚拟字段
userSchema.virtual("fullName").get(function () {
const first = StringUtil.capitalize(this.first.toLowerCase());
const last = StringUtil.capitalize(this.last.toLowerCase());
return `${first} ${last}`;
});
// 使得保存的数据都是一致的小写
userSchema.pre("save", function (next) {
this.username = this.username.toLowerCase();
this.first = this.first.toLowerCase();
this.last = this.last.toLowerCase();
next();
});
export default mongoose.model("user", userSchema);
03 Create, Read, Update, and Delete (CRUD)
npm install moment --save
- 重写dev-server/api/task/tasks-controller, 先使用fake数据测试。
import User from "../../model/user-model";
import Task from "../../model/task-model";
import moment from "moment";
export function index(req, res) {
// FIND ALL TASKS
Task.find({}, (error, tasks) => {
if (error) {
return res.status(500).json();
}
return res.status(200).json({ tasks: tasks });
}).populate("author", "username", "user");
}
export function create(req, res) {
// CREATE TASK
const id = 10;
User.findOne({ _id: id }, (error, user) => {
if (error && !user) {
return res.status(500).json();
}
const task = new Task(req.body.task);
task.author = user._id;
task.dueDate = moment(task.dueDate);
task.save((error) => {
if (error) {
return res.status(500).json();
}
return res.status(201).json();
});
});
}
export function update(req, res) {
// UPDATE TASKS
const id = 10;
User.findOne({ _id: id }, (error, user) => {
if (error) {
return res.status(500).json();
}
if (!user) {
return res.status(404).json();
}
const task = req.body.task;
task.author = user._id;
task.dueDate = moment(task.dueDate);
Task.findByIdAndUpdate({ _id: task._id }, task, (error) => {
if (error) {
return res.status(500).json();
}
return res.status(204).json();
});
});
}
export function remove(req, res) {
// Delete a task
const id = 5;
Task.findOne({ _id: req.params.id }, (error, task) => {
if (error) {
return res.status(500).json();
}
if (!task) {
return res.status(404).json();
}
if (task.author._id.toString() !== id) {
return res
.status(403)
.json({ message: "Not allowed to delete another user's post" });
}
Task.deleteOne({ _id: req.params.id }, (error) => {
if (error) {
return res.status(500).json();
}
return res.status(204).json();
});
});
}
export function show(req, res) {
// GET TASK BY ID
Task.findOne({ _id: req.params.id }, (error, task) => {
if (error) {
return res.status(500).json();
}
if (!task) {
return res.status(404).json();
}
return res.status(200).json({ task: task });
});
}
- 重写dev-server\api\auth\auth-controller.js
import * as StringUtil from "../../utilities/string-util";
import User from "../../model/user-model";
export function index(req, res) {
const validation = validateIndex(req.body);
if (!validation.isValid) {
return res.status(400).json({ message: validation.message });
}
User.findOne({ username: req.body.username.toLowerCase() }, (error, user) => {
if (error) {
return res.status(500).json();
}
if (!user) {
return res.status(401).json();
}
const passwordsMatch = true;
if (!passwordsMatch) {
return res.status(401).json();
}
return res.status(200).json();
});
}
function validateIndex(body) {
let errors = "";
if (StringUtil.isEmpty(body.username)) {
errors += "Username is required. ";
}
if (StringUtil.isEmpty(body.password)) {
errors += "Password is required. ";
}
return {
isValid: StringUtil.isEmpty(errors),
message: errors,
};
}
04 Encrypt Passwords with Node.js and BCrypt
- 重写dev-server/api/register/register-controller
import * as StringUtil from "../../utilities/string-util";
import User from "../../model/user-model";
export function index(req, res) {
const validation = validateIndex(req.body);
if (!validation.isValid) {
return res.status(400).json({ message: validation.message });
}
const user = new User({
username: req.body.username,
password: req.body.password,
first: req.body.first,
last: req.body.last,
});
user.save((error) => {
if (error) {
if (error.code === 11000) {
return res.status(403).json({ message: "Username is already taken" });
}
return res.status(500).json();
}
return res.status(201).json();
});
}
function validateIndex(body) {
let errors = "";
if (StringUtil.isEmpty(body.username)) {
errors += "Username is required. ";
}
if (StringUtil.isEmpty(body.password)) {
errors += "Password is required. ";
}
if (StringUtil.isEmpty(body.first)) {
errors += "First name is required. ";
}
if (StringUtil.isEmpty(body.last)) {
errors += "Last name is required. ";
}
return {
isValid: StringUtil.isEmpty(errors),
message: errors,
};
}
npm install bcrypt-nodejs --save
- 修改dev-server/model/user-model.js
import mongoose from "mongoose";
import { StringUtil } from "../utilities/string-util";
import bcrypt from "bcrypt-nodejs";
const userSchema = new mongoose.Schema({
username: String,
first: String,
last: String,
password: String,
});
userSchema.set("timestamps", true);
// 虚拟字段
userSchema.virtual("fullName").get(function () {
const first = StringUtil.capitalize(this.first.toLowerCase());
const last = StringUtil.capitalize(this.last.toLowerCase());
return `${first} ${last}`;
});
// 使得保存的数据都是一致的小写
userSchema.pre("save", function (next) {
this.username = this.username.toLowerCase();
this.first = this.first.toLowerCase();
this.last = this.last.toLowerCase();
const unsafePassword = this.password;
// 加密password
this.password = bcrypt.hashSync(unsafePassword);
next();
});
export default mongoose.model("user", userSchema);
- 使用postman测试Post http://localhost:3000/api/register
另外打开mongo控制台,查询结果如下,可以看到password已经加密
> use mevn-dev-db
switched to db mevn-dev-db
> show collections
users
> db.users.find().pretty()
{
"_id" : ObjectId("607fe876fa8ab681e0932de6"),
"username" : "morpheus",
"password" : "$2a$10$p6pmj2aohiF/m1BvgNIz1eoXX.a54BiXu26FkWZ9sWKhzdaBYuTne",
"first" : "morpheus",
"last" : "cao",
"createdAt" : ISODate("2021-04-21T08:55:18.719Z"),
"updatedAt" : ISODate("2021-04-21T08:55:18.719Z"),
"__v" : 0
}
05 Validate Passwords
npm install axios --save
- 修改dev-server/api/auth/auth-controller.js
import * as StringUtil from "../../utilities/string-util";
import User from "../../model/user-model";
import bcrypt from "bcrypt-nodejs";
export function index(req, res) {
// console.log(req.body, req.body.username.toLowerCase());
const validation = validateIndex(req.body);
if (!validation.isValid) {
return res.status(400).json({ message: validation.message });
}
User.findOne({ username: req.body.username.toLowerCase() }, (error, user) => {
console.log(user);
if (error) {
return res.status(500).json();
}
if (!user) {
return res.status(401).json();
}
/* const passwordMatch = true;
babel编译调用错误
const passwordMatch = User.passwordMatches( req.body.password, user.password );
验证密码是否正确 */
const passwordMatch = bcrypt.compareSync(req.body.password, user.password);
if (!passwordMatch) {
return res.status(401).json({ message: "User not exist!" });
}
return res.status(200).json({ message: "Login sucessed!" });
});
}
function validateIndex(body) {
let errors = "";
if (StringUtil.isEmpty(body.username)) {
errors += "Username is required. ";
}
if (StringUtil.isEmpty(body.password)) {
errors += "Password is required. ";
}
return {
isValid: StringUtil.isEmpty(errors),
message: errors,
};
}
- 新建src\services\HttpService.js
import store from "../store";
import axios from "axios";
export default function http() {
return axios.create({
baseURL: store.state.apiUrl,
});
}
- 修改src\services\AuthService.js
import store from "../store";
import http from "./HttpService";
export function isLoggedIn() {
const token = localStorage.getItem("token");
return token != null;
}
/* export function login() {
const token = {
username: "morpheus",
};
setToken(token);
} */
export function login(user) {
console.error("AuthService->login:" + JSON.stringify(user));
debugger;
return http()
.post("/auth", user)
.then((res) => {
if (res) {
const fakeToken = {
token: "my-token",
};
setToken(fakeToken);
}
});
}
function setToken(token) {
localStorage.setItem("token", JSON.stringify(token));
store.dispatch("authenticate");
}
export function getUsername() {
return "morpheus";
}
export function getUserId() {
return 1;
}
export function registerUser(user) {
return http().post("/register", user);
}
- 替换src\views\authentication\Register.vue
<template>
<div>
<h1>Register Route</h1>
<form class="custom-form" v-on:submit.prevent="onSubmit">
<div class="form-group">
<label for="first">First Name</label>
<input
v-model="first"
type="text"
class="form-control"
id="first"
placeholder="First Name"
/>
</div>
<div class="form-group">
<label for="last">Last Name</label>
<input
v-model="last"
type="text"
class="form-control"
id="last"
placeholder="Last Name"
/>
</div>
<div class="form-group">
<label for="username">Username</label>
<input
v-model="username"
type="text"
class="form-control"
id="username"
placeholder="Username"
/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
v-model="password"
type="password"
class="form-control"
id="password"
placeholder="Password"
/>
</div>
<div class="form-group">
<button type="submit" class="btn btn-secondary">Register</button>
</div>
</form>
</div>
</template>
<script>
import * as auth from "../../services/AuthService.js";
export default {
name: "register",
data: function () {
return {
username: "",
password: "",
first: "",
last: "",
};
},
methods: {
/*
onSubmit: function(event) {
event.preventDefault();
auth.login();
this.$router.push({ name: 'home' });
}
*/
onSubmit: async function () {
const user = {
username: this.username,
password: this.password,
first: this.first,
last: this.last,
};
const registerPromise = auth.registerUser(user);
const loginPromise = auth.login(user);
await Promise.all([registerPromise, loginPromise]);
this.$router.push({ name: "home" });
},
},
};
</script>
- 替换src\views\authentication\Login.vue
<template>
<div>
<h1>Login Route</h1>
<form class="custom-form" v-on:submit.prevent="onSubmit">
<div class="form-group">
<label for="username">Username</label>
<input
v-model="username"
type="text"
class="form-control"
id="username"
placeholder="Username"
/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
v-model="password"
type="password"
class="form-control"
id="password"
placeholder="Password"
/>
</div>
<div class="form-group">
<button type="submit" class="btn btn-secondary">Submit</button>
</div>
</form>
</div>
</template>
<script>
import * as auth from "../../services/AuthService";
export default {
name: "login",
data: function () {
return {
username: "",
password: "",
};
},
methods: {
onSubmit: async function () {
const user = {
username: this.username,
password: this.password,
};
// event.preventDefault();
await auth.login(user);
this.$router.push({ name: "home" });
},
},
};
</script>