打通全栈
打通全栈
这一节算是前面的几节内容的综合使用。所谓全栈项目,要有这几层技术组成:
- 最贴近用户的,是 React
- HTTP 请求,用 axios
- 后台,我们使用 express
- 最底层,海量数据,用 Mongodb
使用 Mongoose 来查询所有用户
首先,保证 mongodb 处于运行状态,然后,通过 mongo-express 查看一下, mongodb 中是否有多个用户。
成熟的工程师,你给他 Mongoose 的 API 文档 他就有能力完成这个任务了。但是如果你属于开发新手,还是要先通过教程,来学习一些最基本的使用,后续才会有能力看 API 文档。
参考:http://haoqicat.com/react-express-api/5-rest-api
到后台代码的 index.js 文件中,把 db.once 部分的修改成下面这样:
db.once('open', function() {
User.find().exec(function(err, users) {
console.log(users);
});
});
这样,我们到 express-backend 文件夹中,运行,可以看到如下输出结果
$ node index.js
running on port 3000...
[ { _id: 584b62b830a2a2cbf4c4c3f6,
username: 'billie66',
email: 'billie@billie66.com' },
{ _id: 584bb045ff8f0f1c7ba4fe24,
username: 'inCode',
email: 'inCode@incode.com',
__v: 0 } ]
可以看到,终端中可以打印出所有数据文档。证明我们的 mongoose 的 find() 接口 使用正确。
但是,我们为何不把代码写成这样呢?
let users = User.find();
conole.log(users)
答案是: find() 接口是一个异步函数,所以它的返回值 users 只能 在回调函数中使用。.exec 字面意思就是执行,我们把回调函数传给它做参数。
用 API 来返回 JSON
上面数据虽然拿到,但是如果想提供给客户端使用:
- 第一步,把它要封装成一个 API
- 第二步,数据格式转换为 JSON
先来做第一步,代码做出如下调整:
db.once('open', function() {
console.log('success');
});
app.get('/users', function(req, res){
User.find().exec(function(err, users) {
console.log(users);
});
})
上面把 User.find() 代码封装到了一个 API ( Web API ) 。这样, 触发条件就变了。只有当客户端发出 GET /users 请求的时候,User.find() 代码才会被执行。
暂时,我们用 curl 来模拟一下客户端请求:
curl -X GET http://localhost:3000/users
但是,此时,curl 请求不到任何返回信息,因为 console.log(users) 只会把 信息打印到后台终端。curl 请求不到信息,未来浏览器也就请求不到。所以要把这一行 改为
// res.send() 可以数据返回给客户端,但是我们要的是 json ,所以用下面接口
res.json()
也就是要写成这样:
app.get('/users', function(req, res){
// res.json({"users": "happypeter"});
User.find().exec(function(err, users) {
res.json({users});
});
})
这样,再用 curl 请求一下,前台就能读到数据了,如下
$ curl -X GET http://localhost:3000/users
{"users":[{"_id":"584b62b830a2a2cbf4c4c3f6","username":"billie66","email":"billie@billie66.com"},{"_id":"584b760498d7b520b68a05cd","username":"pppaaa"},{"_id":"584bb045ff8f0f1c7ba4fe24","username":"inCode","email":"inCode@incode.com","__v":0}]}
这样,后台代码就准备完毕。
后台代码
express-backend 文件夹中,有下面的文件:
index.js
const express = require('express');
const app = express();
const cors = require('cors');
app.use(cors());
const mongoose = require('mongoose');
const User = require('./models/user');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost:27017/digicity');
// 执行此行代码之前,要保证 mongodb 数据库已经运行了,而且运行在 27017 端口
var db = mongoose.connection;
db.on('error', console.log);
db.once('open', function() {
console.log('success');
});
// 下面三行就是我们实现的一个 API
app.get('/users', function(req, res){
// res.json({"users": "happypeter"});
User.find().exec(function(err, users) {
res.json({users});
});
})
app.listen(3000, function(){
console.log('running on port 3000...');
});
models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const UserSchema = new Schema(
{
username: { type: String },
email: { type: String }
}
);
module.exports = mongoose.model('User', UserSchema);
// `User` 会自动对应数据库中的 users 这个集合
// 如果这里是 Apple 那么就会对应 apples 集合
// 如果这里是 Person 那么就会对应 people 集合
前台书写 axios 请求
到 react-frontend/ 也就前台项目中,修改生命周期函数如下:
componentWillMount() {
axios.get('http://localhost:3000/users').then((response) => {
console.log(response);
// this.setState({username: response.data.username});
})
}
这样,就可以看出 response 中的数据结构了,我们想要的数据可以这样拿到
constructor(){
super();
this.state = {
users: []
};
}
componentWillMount() {
axios.get('http://localhost:3000/users').then((response) => {
this.setState({users: response.data.users});
})
}
使用 map 展开数组
render 函数做如下调整:
render(){
const userList = this.state.users.map((user, i) => {
return (
<div key={i}>
username:
{user.username}
</div>
)
});
return(
<div>
{ userList }
</div>
)
}
这样,页面中就可以显示出所有用户的用户名的列表了。
前台向后台传递参数
前台在发 axios 的 http 请求的时候,我们要携带一个参数到服务器端。这里,参数的本质就是字符串。所以这一节就是把一个任意字符串,从前台传到后台。
比如,我们现在想把一个 “123456” 这个字符串,传递一下。
前台发送请求
那么前台的请求我们可以这样写
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class App extends Component {
handleClick(e){
e.preventDefault();
console.log('handleClick......');
axios.get('http://localhost:3000/users/123456').then((response) => {
console.log(response);
})
}
render(){
return(
<div onClick={this.handleClick.bind(this)}>
clickme
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('app'));
解释一下:这里我们传递的字符串是 123456 但是未来还可能替换成 任意的字符串
后台代码
const express = require('express');
const app = express();
const cors = require('cors');
app.use(cors());
app.get('/users/:id', function(req, res){
console.log(req.params.id)
})
app.listen(3000, function(){
console.log('running on port 3000...');
});
说明:req 是 request 请求的缩写,express 用这个变量来接收前台发过来的请求。 res 是 response 响应的缩写,用来响应前台发过来的请求,实际作用就是往前台发送数据。
注意,前台的请求是:
GET /users/123456
后台如果 API 写成这样:
app.get('/users'...)
是匹配不上的。可以写成
app.get('/users/123456'...)
但是这样前台能传递的字符串就限制死了。所以最好就是使用 express 的 params (param 是英文参数的简写) 机制来进行处理,也就是写成如上的
app.get('/users/:id')
注:上面的 id 写成其他的单词也可以,关键点就是前面的冒号。这样写之后 前台请求中的字符串,就可以在后台通过 req.params.id 这个变量来拿到了。
前台能向后台传递数据的方式并不唯一。我们这里介绍的是最简单的一种。
显示一个用户的信息
以下知识的综合应用
- MongoDB 数据库基本操作
- Mongoose 这个 JS 库的使用
- 前台向后台传递参数:涉及 axios 和 express 配合
- React 基本操作
我们要完成的任务是,用户在浏览器中点按钮,发 axios 请求访问这个链接:
http://localhost:3000/users/一个真实的用户 id
页面上要能显示出用户名和邮箱。
前台代码
我们先打开 mongo-express ,拷贝一个真实的 id 出来。然后粘贴到 前台代码的 axios 请求中
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
user: {}
};
}
handleClick(e){
e.preventDefault();
axios.get('http://localhost:3000/users/584dfb6fd5aa1c13955d5cca').then((response) => {
console.log(response);
this.setState({
user: response.data
});
})
}
render(){
return(
<div>
<div onClick={this.handleClick.bind(this)}>
clickme
</div>
<div>
username:
{ this.state.user.username }
</div>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('app'));
后台增加请求一位用户信息的API
src/index.js
const express = require('express');
const app = express();
const cors = require('cors');
app.use(cors());
const mongoose = require('mongoose');
const User = require('./models/user');
mongoose.connect('mongodb://localhost:27017/digicity');
// 执行此行代码之前,要保证 mongodb 数据库已经运行了,而且运行在 27017 端口
var db = mongoose.connection;
db.on('error', console.log);
db.once('open', function() {
console.log('success');
});
// 下面三行就是我们实现的一个 API
app.get('/users/:id', function(req, res){
User.findById(req.params.id,function (err,user) {
console.log(user);
res.json(user)
})
})
app.listen(3000, function(){
console.log('running on port 3000...');
});
models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const UserSchema = new Schema(
{
username: { type: String },
email: { type: String }
}
);
module.exports = mongoose.model('User', UserSchema);
package.json
{
"name": "express-backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.1",
"express": "^4.14.0",
"mongoose": "^4.7.2"
}
}
总结
这样,前台打开 index.html 文件,点一下 clickme ,就可以在页面上 显示出从后台 mongodb 中取出的用户的 username 了。