Isomorphic JavaScript with MEVN stack,第三部分------连接Mongodb

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

postman设置Content-Type

post设置Body的json数据

另外打开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>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值