2019独角兽企业重金招聘Python工程师标准>>>
后台
前后端分离之后,后台语言的选择一下子多了,只要能处理请求,返回JSON就可以了。不过据说PHP是世界上最好的语言,所以本教程选的是PHP,当然,站在巨人的肩膀上,选个好用不浮夸的框架也是必要的,不然重复发明轮子会占用很多玩耍的时间,不利于身心健康。性能什么的,不要计较太多,如果幸福的烦恼来临,多花点钱,多搞几台机器的事而已。废话不多说,撸起袖子加油干!
1.安装Yii2
Yii2通过composer-asset-plugin整合bower和npm上的前端库到composer的安装上,如果之前安装过这个插件,就不用安装啦,直接
composer create-project --prefer-dist yiisoft/yii2-app-basic basic
如果没有安装过asset-plugin,就先安装插件,再创建一个新的Yii2程序:
composer global require "fxp/composer-asset-plugin:^1.2.0"
composer create-project --prefer-dist yiisoft/yii2-app-basic basic
稍等片刻,就能安装好啦。 Composer安装有问题的,请看另外一篇博文:Composer更新慢的终极解决方案
配置下虚拟主机,以apache为例,打开apache目录下的httpd-vhosts.conf配置文件,添加配置:
ServerAdmin [email protected]
DocumentRoot "D:/devs/web/basic/web"
ServerName basic.backend.local
ErrorLog "d:/devs/web/logs/gale.local-error.log"
CustomLog "d:/devs/web/logs/gale.local-access.log" common
DocumentRoot指向的是刚刚创建的Yii2程序的web目录,ServerName是本地域名,配置好apache配置后,需要再改一下hosts文件,以Win7为例,打开 C:\Windows\System32\drivers\etc\hosts 在文件最后添加一行
127.0.0.1 basic.backend.local
保存,重启Apache,打开浏览器,打开地址 http://basic.backend.local/ 看到熟悉的 Congratulations!大字,说明安装配置完成啦!
2.配置Yii2支持API调用
2.1 添加API相关Controller的基础类,这里取名BaseAPIController:
namespace app\controllers;
use yii\filters\ContentNegotiator;
use yii\rest\Controller;
use yii\web\Response;
use Yii;
class BaseAPIController extends Controller
{
public function behaviors()
{
$behaviors = parent::behaviors();
unset($behaviors['authenticator']);
$behaviors['corsFilter'] = [
'class' => \yii\filters\Cors::className(),
'cors' => [
// restrict access to
'Access-Control-Request-Method' => ['*'],
// Allow only POST and PUT methods
'Access-Control-Request-Headers' => ['*'],
// Allow only headers 'X-Wsse'
'Access-Control-Allow-Credentials' => true,
// Allow OPTIONS caching
'Access-Control-Max-Age' => 3600,
// Allow the X-Pagination-Current-Page header to be exposed to the browser.
'Access-Control-Expose-Headers' => ['X-Pagination-Current-Page'],
],
];
$behaviors['contentNegotiator'] = [
'class' => ContentNegotiator::className(),
'formats' => [
'application/json' => Response::FORMAT_JSON
]
];
return $behaviors;
}
}
主要解决两个问题:
- cors问题,就是跨域调用的问题,这个问题可大可小,展开了还能再写很多,这里不细说了。
- 返回的数据类型,本例所以请求都返回JSON格式数据。
2.1.2 修改接受的请求数据格式
传统的web应用,请求类型通常是x-www-form-urlencoded与multipart/form-data, 对应普通的表单提交,以及文件内容提交,默认Yii2不支持Json格式的请求,需要在config/web.php里修改request组件配置 :
'request' => [
'cookieValidationKey' => 'Dk7BQ6_hk9YRPMdkaaK6FFwIpa123456',
'parsers' => [
'application/json' => 'yii\web\JsonParser',
'text/json' => 'yii\web\JsonParser',
],
],
2.2 添加权限验证控制器
namespace app\controllers;
class AuthController extends BaseAPIController
{
public function actionIndex()
{
$username = \Yii::$app->request->post('name');
$password = \Yii::$app->request->post('password');
if($username == "admin" && $password == "admin")
{
return ['success'=>1,'msg'=>'100-token'];
}
return ['success'=>0,'msg'=>\Yii::t('erp','Username or password error')];
}
}
简单起见,不涉及数据库操作,用户名密码如果都符合要求,就返回一个token,这个token的值是100-token,为什么是这个值,请看创建好的Yii2项目的Models目录下的User.php.
2.3 添加需要授权才能访问的控制器
namespace app\controllers;
use Yii;
use yii\filters\auth\HttpBearerAuth;
class ItemController extends BaseAPIController
{
public function behaviors()
{
$behaviors = parent::behaviors();
if (Yii::$app->getRequest()->getMethod() !== 'OPTIONS') {
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
];
}
return $behaviors;
}
public function actionIndex()
{
return ['success'=>1,'msg'=>'hello'];
}
}
重点是,这个ItemController只有通过验证,才能访问到Index这个action,这里增加的验证器HttpBearerAuth就是用来验证请求的Header里是否带着刚刚分发的token, 之所以不对OPTIONS请求做验证,是因为OPTIONS请求不带别的信息啊,没法验证。浏览器非要在请求之前发个OPTIONS请求,也没办法。
2.4 测试
打开浏览器访问http://basic.backend.local/item 正常情况下,会返回为授权信息 :
{"name":"Unauthorized","message":"Your request was made with invalid credentials.","code":0,"status":401,"type":"yii\\web\\UnauthorizedHttpException"}
前台
前端采用Vue2这个单独好用的js框架,文档丰富,社区活跃,最难得的是作者很帅!
1.安装Node和vue-cli
现在前端没有Node简直寸步难行,所以Node是必须安装的,去官网下个安装包,安装好。然后再安装vue-cli工具。
2.创建Vue单页面程序
运行命令:
vue init webpack-simple basic_front
会让您输入必要的信息,如项目名称,描述,作者之类的。这里简单起见,一路默认回车就是啦。
D:\devs\web>vue init webpack-simple basic_front
> Project name basic
> Project description A Vue.js project
> Author elvis_lim
> Use sass? No
vue-cli · Generated "basic_front".
To get started:
cd basic_front
npm install
npm run dev.
- 运行程序
如上面命令行输出的提示,vue-cli创建项目后,还需要npm install安装,然后npm run dev运行,看看能不能成功运行。
4.安装必要库
本例需要用到Vue-router这个官方推荐路由,以及axios这个官方推荐http库。至于官方为什么不推荐原来的vue-resource了,我也不知道,前端喜新厌旧,就是这样的。
npm install axios bootstrap iview vue-router --save
细心的朋友会发现多安装了bootstrap 和 iview这两个库,没别的目的,就是想让界面好看点,听说也比较火,安装了吧,反正闲着也是闲着。
稍等片刻,安装完毕,npm run dev看看效果吧。
5.开始添加前端逻辑
逻辑是这样的,这个前端页面,是登录之后才能用的,未登录用户,直接给跳转到登录页。登录成功后,跳回首页,然后调用Item/index接口,获取信息然后显示在页面上。
好了,计划完毕,开始写代码。在src目录下创建一个config目录,加一个setting.js, 内容如下:
export default{
remoteHost:'http://basic.backend.local',
userToken:'tk'
}
基本的配置信息就在这里了,包含了后端服务地址,用户标志的key名称。
再在src目录下创建一个services目录,加一个auth.js,用来辅助判断用户登录状态:
import setting from '../config/setting.js'
export default {
login(data){
localStorage.setItem(setting.userToken,data)
},
// authentication status
authenticated(){
var t = localStorage.getItem(setting.userToken);
return t && t.length > 0;
},
getToken(){
return localStorage.getItem(setting.userToken);
},
logout(){
localStorage.setItem(setting.userToken,"");
}
}
就四个函数,功能不言而喻。
再在services目录下加一个http.js,用来包装一下axios:
import axios from 'axios'
import setting from '../config/setting.js'
import router from '../main.js'
import Auth from './auth.js'
// axios 配置
axios.defaults.timeout = 5000;
axios.defaults.baseURL = setting.remoteHost;
// http request 拦截器
axios.interceptors.request.use(
config => {
if (Auth.authenticated()) {
var token = Auth.getToken();
config.headers.common["Authorization"] = `Bearer ${token}`;
}
return config;
},
err => {
return Promise.reject(err);
}
);
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 401 清除token信息并跳转到登录页面
Auth.logout()
router.replace({
path: 'login',
query: {redirect: router.currentRoute.fullPath}
})
}
}
console.log(error);//console : Error: Request failed with status code 402
return Promise.reject(error)
});
export default axios;
重点是在request和response两个拦截器。request里,查看用户是处于登录状态,如果是,那么就在请求的headers里加上 Bearer ${token}
; 这样后端在接受到请求的时候,用HttpBearerAuth去校验这个token是不是符合要求,就能够进行用户权限验证啦。当然,不用拦截器也是可以的,但是每个请求都需要加Headers头,会不会很累?
response拦截器里,检查返回的状态,如果是401状态码,就是用户没有通过权限验证,那就去登录页面再登录啦。同样的,每个请求也是可以检查状态码的,但是太麻烦,偷懒是美德!
最后,在src目录下的main.js里把这些工具函数连接起来:
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import http from './services/http.js'
import iView from 'iview';
import 'iview/dist/styles/iview.css';
import 'bootstrap/dist/css/bootstrap.css'
import Login from './components/Login.vue'
import Home from './components/Home.vue'
Vue.prototype.$http = http
Vue.use(VueRouter)
Vue.use(iView)
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{
path: '/',
component: Home,
meta: {
requireAuth: true
}
},
{
path: '/login',
component: Login
}
]
})
import Auth from './services/auth.js';
router.beforeEach((to, from, next) => {
if(to.meta.requireAuth && !Auth.authenticated())
{
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
else {
next()
}
})
new Vue({
el: '#app',
router: router,
render: h => h(App)
});
export default router
写完这一大串后,发现命令行里报了一堆错,看了看,应该是引入了bootstrap,iview的css,而默认的webpack配置文件没有相应的loader, 于是先安装下这些loader:
npm install css-loader style-loader --save-dev
然后,修改webpack.config.js文件,给file-loader加上woff|woff2|ttf|eot,因为bootstrap用到了这些字体文件,然后再加上style-loader!css-loader, 修改如下:
{
test: /\.(png|jpg|gif|svg|woff|woff2|ttf|eot)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
},
{
test: /\.css$/,
loader: 'style-loader!css-loader'
}
再次npm run dev后,熟悉的首页又回来啦。
6.添加路由根节点
打开App.vue,修改后的App.vue如下:
其实就是用App这个组件作为总的树根,然后Home和Login两个组件作为路由子组件加载进来。
7.添加components目录,再添加Login.vue和Home.vue
Login
Home.vue
Home Page
{{msg}}
logout
添加完毕后,npm run dev 就可以测试了。按照之前设计的逻辑,首先请求,http://localhost:8080, 由于/路由对应的是Home.vue,而Home组建创建的时候,调用了一个后端需要权限的API,于是返回401错误,然后被重定向到login去登录。
登录的时候,发现报错:
XMLHttpRequest cannot load http://basic.backend.local/auth/index. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 404
意思是说跨域了,http://localhost:8080, 不被后端接受。跨域问题有很多解决方法,本人倾向于用应用服务器配置解决,这里以 Apache为例,在后端的web目录下,加上一.htaccess文件,内容如下:
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Headers: "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php
告诉Apache,不要管跨域问题啦。Nginx的话,添加:
location / {
if ($request_method = OPTIONS ) {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "*";
add_header Access-Control-Allow-Headers "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding";
add_header Access-Control-Allow-Credentials "true";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 200;
}
try_files $uri $uri/ /index.php?$args;
}
跨越问题其实挺复杂, 建议多看看相关资料,这里就不多说了。
加了.htaccess文件后,刷新下页面,发现来到了登录页面,这就对啦。
继续用admin,admin登录。相信就能正常啦。
后记
本文花了1天时间写成,踩坑花了至少3天时间,如果您读到这里,恭喜您,您是一个有耐心和爱学习的人。没有更多内容了。代码已上传到码云,随意获取查看:
前台:http://git.oschina.net/linwx/basic_front 后台:http://git.oschina.net/linwx/basic_backend
希望您也能感受到使用Vue2和Yii2快速开发的乐趣!