从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

时间:2022-11-11 17:44:48

壹周回顾

哈喽,又是元气满满的一个周一,又与大家见面了,周末就是团圆节了,正好咱们的前后端也要团圆了,为什么这么说呢,因为以后的开发可能就需要前后端一起了,两边也终于会师了,还有几天Vue系列就基本告一段落了,大家也好好加油鸭,今天将的内容呢,其实细心的你看到题目应该就能大家猜到了,前提是一直看本系列的小伙伴们,包括之前.net core部分,这里先简单说下上周咱们都说了什么:

周一:《十九║Vue基础: 样式动态绑定+生命周期》重点说了下 Vue 开发中的八个生命周期,这个是一个重点,希望大家可以多看看,这个在以后的开发中会经常遇到;

周二:《二十║Vue基础终篇:组件详解+项目说明》重点说了下组件的使用,包括定义、传值、使用等等,这个更是重中之重,组件的使用在 Vue 的开发中必不可少;

周三:《二十一║Vue实战:开发环境搭建【详细版】》详细的说了下开发环境的搭建,不仅讲了如何搭建,还详细的说明了每一个工具、插件的使用意义;

周四:《二十二║Vue实战:个人博客第一版(axios+router)》根据周三搭建的环境,第一次创建了咱们第一版的个人博客,封装了 axios ,第一次连接上了咱们之前的 .net core api;

周五:《二十三║Vue实战:Vuex 其实很简单》通过一个小 DEMO 说明了 Vuex 是如何对我们的 Vue 实行状态化管理的,让大家对其使用有了一定的了解,为在以后的大项目中使用打下基础;

周五的时候,咱们通过对表单的组件化,来说明了 vuex 的存在意义,今天咱们还是会用到这个 vuex ,而且还会配合着 .net core api,到底是什么呢?请看今天的讲解。

注意:周四的时候,只写了个人博客的首页,周末的时候,已经把详情页更新了,大家可以自行去 Git 查看,文末有地址

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

零、今天要完成右下角粉色区块的部分

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

一、如何实现权限验证的过程

大家一定还记得之前在 .net core api 系列文章中《框架之五 || Swagger的使用 3.3 JWT权限验证【修改】》,咱们通过对 JWT 的讲解,实现了对接口的验证,大家可以去了解一下,当时因为是没有前端,所以咱们就直接用的 Swagger 接口文档来手动设置的权限验证,当时群里有很多小伙伴对这个不是很明白,我也是简单说了下,通过手动在 swagger 中输入Header ,变成每次 vue 的 axios 请求的 Header 中添加 Token,这个 Token 就是咱们手动配置的那个,因为当时没有前后端搭配,所以只是比较笼统的说了下这个流程,今天呢,就重点来说下这个授权登录验证,也为下边的管理后台铺路,这里配合 Vue 前端,再仔细讲讲是如何实现前后端同时验证的:

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

上图中说的也是很详细了,主要分为两个验证:

1、前端验证(蓝色部分),用户访问一个页面,首先判断是否需要验证登录,比如管理后台,或者订单系统的下单页(首页和详情页自然是不需要用户登录的,购物车和订单等必须登录,当然有些游客也可以购买的除外),然后去验证是否存在 Token,存在就添加到 axios 的 Header 中去请求后端 API,反之则去登录页登录;

2、后端验证(绿色部分),这个就是咱们之前在说 .net core api 的时候说到的 JWT 授权验证,根据当前前端 axios 请求中传来的 Token ,解析出是否被篡改,以及是否会相应的权限,这样就可以进一步返回数据了;

这个时候大家一定会有疑惑了,既然现在每一个接口都定义了权限,为什么要俩边都需要验证,只需要后端 api 一个验证不就行了,何必这么麻烦?我认为是这样的:

首先前端验证的主要目的是:通过手动配置,可以让用户去主动获取 Token ,不用每次都去获取,而且也减轻了后端请求的次数,总不能是先去发送请求,再判断当前页面是否需要登录吧,嗯,总结来说,

前端是为了页面级登录,后端是为了接口级验证,而且也是想把 vue 前端工程化的思想。

二、结合API设计登录页 —— 实现后端验证

1、引入 ElementUI 样式框架

因为之后需要一个管理后台,所以考虑着增加一个框架,目前比较流行的就是 ElementUI 和 IView,今天咱们先说一下引用 ElementUI

首先,在项目中 执行 npm install,初始化以后,在 node_modules 中查看是否存在 element-ui 文件夹,如果没有,则执行

 npm i element-ui -S

然后就可以看到项目中已经成功安装 elementui 了

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

然后、在项目的入口配置文件 main.js 中,引用

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

如果项目没有报错,到此则安装成功。

2、添加统一登录页面

第一、在 src 的 views 文件夹内,添加 Login.vue 页面,并添加内容:

<template>
<el-row type="flex" justify="center">
<el-form ref="loginForm" :model="user" :rules="rules" status-icon label-width="50px">
<el-form-item label="账号" prop="name">
<el-input v-model="user.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pass">
<el-input v-model="user.pass" type="password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-upload" @click="login">登录</el-button>
</el-form-item>
</el-form>
</el-row>
</template> <script>
export default {
methods: {
login() {//使用elementui validate验证
this.$refs.loginForm.validate(valid => {
if (valid) {//这里在下边会改写成登录信息 感谢 @风格不同 提醒注释错误问题
if (this.user.name === "admin" && this.user.pass === "") {
this.$notify({
type: "success",
message: "欢迎你," + this.user.name + "!",
duration:
});
this.$router.replace("/");
} else {
this.$message({
type: "error",
message: "用户名或密码错误",
showClose: true
});
}
} else {
return false;
}
});
}
},
data() {
return {
user: {},//配合页面内的 prop 定义数据
rules: {//配合页面内的 prop 定义规则
name: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
pass: [{ required: true, message: "密码不能为空", trigger: "blur" }]
}
};
}
};
</script>

添加路由后,测试页面是否可行

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

3、配合后台登录请求

完善  BlogController.cs 页面,稍微调整了下接口,和之前的没有差别,并增加权限验证

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

        /// <summary>
/// 获取博客列表
/// </summary>
/// <param name="id"></param>
/// <param name="page"></param>
/// <param name="bcategory"></param>
/// <returns></returns>
[HttpGet]
public async Task<object> Get(int id, int page = , string bcategory = "技术博文")
{
int intTotalCount = ;
int TotalCount = ;
List<BlogArticle> blogArticleList = new List<BlogArticle>(); if (redisCacheManager.Get<object>("Redis.Blog") != null)
{
blogArticleList = redisCacheManager.Get<List<BlogArticle>>("Redis.Blog");
}
else
{
blogArticleList = await blogArticleServices.Query(a => a.bcategory == bcategory);
redisCacheManager.Set("Redis.Blog", blogArticleList, TimeSpan.FromHours());
} TotalCount = blogArticleList.Count() / intTotalCount; blogArticleList = blogArticleList.OrderByDescending(d => d.bID).Skip((page - ) * intTotalCount).Take(intTotalCount).ToList(); foreach (var item in blogArticleList)
{
if (!string.IsNullOrEmpty(item.bcontent))
{
int totalLength = ;
if (item.bcontent.Length > totalLength)
{
item.bcontent = item.bcontent.Substring(, totalLength);
}
}
} var data = new { success = true, page = page, pageCount = TotalCount, data = blogArticleList }; return data;
} // GET: api/Blog/5
/// <summary>
/// 获取详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id}", Name = "Get")]
public async Task<object> Get(int id)
{
var model = await blogArticleServices.getBlogDetails(id);
var data = new { success = true, data = model };
return data;
}

调整 LoginController.cs 的获取 Token 方法:

  /// <summary>
/// 获取JWT的方法
/// </summary>
/// <param name="id">id</param>
/// <param name="sub">角色</param>
/// <returns></returns>
[HttpGet]
[Route("Token")]
public JsonResult GetJWTStr(string name, string pass)
{
string jwtStr = string.Empty;
bool suc = false;
//这里就是用户登录以后,通过数据库去调取数据,分配权限的操作
//这里直接写死了
if (name == "admins" && pass == "admins")
{
TokenModelJWT tokenModel = new TokenModelJWT();
tokenModel.Uid = ;
tokenModel.Role = "Admin"; jwtStr = JwtHelper.IssueJWT(tokenModel);
suc = true;
}
else
{
jwtStr = "login fail!!!";
}
var result = new
{
data = new { success = suc, token = jwtStr }
}; return Json(result);
}

4、修改 前端的 Login.vue 页面的登录方法,获取到 Token ,并把其保存到 Vuex 中

<template>
<el-row type="flex" justify="center">
<el-card v-if="isLogin">
欢迎:admins
<br>
<br>
<el-button type="primary" icon="el-icon-upload" @click="loginOut">退出登录</el-button>
</el-card>
<el-form v-else ref="loginForm" :model="user" :rules="rules" status-icon label-width="50px">
<el-form-item label="账号" prop="name">
<el-input v-model="user.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pass">
<el-input v-model="user.pass" type="password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-upload" @click="login">登录</el-button>
</el-form-item>
</el-form>
</el-row>
</template> <script>
export default {
methods: {
login: function() {
let that = this;
that.$store.commit("saveToken", "");//清掉 token
this.$refs.loginForm.validate(valid => {
if (valid) {
this.$api.get(
"Login/Token",
{ name: that.user.name, pass: that.user.pass },
r => {
if (r.data.success) {
var token = r.data.token;
that.$store.commit("saveToken", token);//保存 token
this.$notify({
type: "success",
message: "欢迎你," + this.user.name + "!",
duration:
});
console.log(that.$store.state.token);
this.$router.replace("/");
} else {
this.$message({
type: "error",
message: "用户名或密码错误",
showClose: true
});
}
}
);
} else {
return false;
}
});
},
loginOut(){
this.isLogin=false;
this.$store.commit("saveToken", "");//清掉 token }
},
data() {
return {
isLogin:false,
user: {},
rules: {
name: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
pass: [{ required: true, message: "密码不能为空", trigger: "blur" }]
}
};
},
created() {
if (window.localStorage.Token&&window.localStorage.Token.length>=){
this.isLogin=true;
}
} };
</script>

5、修改 vuex 仓库,把 token 存进store中

import Vue from "vue";
import Vuex from "vuex"; Vue.use(Vuex); const store = new Vuex.Store({
// 初始化的数据
state: {
formDatas: null,
token: ""
},
// 改变state里面的值得方法
mutations: {
getFormData(state, data) {
state.formDatas = data;
},
saveToken(state, data) {
state.token = data;
window.localStorage.setItem("Token", data);//就是这里,保存到了 localStorage 中
}
}
});
// 输出模块
export default store;

6、这个时候要修改下之前我们封装的 http.js 方法,因为当时我们过滤掉了失败的方法,这里要打开下,大家自行修改下

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

这个时候,我们再登录的话,已经发生变化

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

这个时候大家可以看到,我们成功的登录了(右上角有欢迎提示),然后 token 也成功的保存到 stroe/localStorage 里(下边控制台输出),

因为我们在博客页增加了权限,虽然我们是用的 admin 账号,但是 Header 中还没有添加Token,所以现在还是 401,那如何才能有效的增加请求 Header 呢,请往下看,权限验证前端部分。

三、实现一:登录拦截验证——路由拦截

1、修改 router.js 路由,实现按需登录

在需要登录的地方,增加登录要求字段,

然后增加 beforeEach 钩子函数(这里有一个问题,只能获取到本地缓存数据,无法获取 Vuex ,正在研究中)

import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";
import FormVuex from "./views/FormVuex.vue";
import Content from "./views/content";
import Login from "./views/Login";
import store from "./store"; Vue.use(Router); const router = new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "home",
component: Home,
meta: {
requireAuth: true // 添加该字段,表示进入这个路由是需要登录的
}
},
{
path: "/Vuex",
name: "Vuex",
component: FormVuex
},
{
path: "/Content/:id",
name: "Content",
component: Content
},
{
path: "/Login",
name: "Login",
component: Login
},
{
path: "/about",
name: "about",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "./views/Form.vue")
}
]
}); router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
if (window.localStorage.Token&&window.localStorage.Token.length>=128) { // 通过vuex state获取当前的token是否存在
next();
}
else {
next({
path: '/login',
query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由
})
}
}
else {
next();
}
}) export default router;

2、修改 http.js 封装方法,自动在请求中把 Token 添加到 Header 中

上边的路由设置,仅仅是对 Token 进行判断,还没有添加到 Header 里,更没有进行验证

注意:目前是用的 localStorage 本地存储的方法(这一点是受到微信小程序的启发),

但是直接在 router.js 中直接获取 store 的token属性,取不到,有知道的小伙伴请留言

更新:经过群里小伙伴的提醒 还有楼下评论席@路遥心安 的提醒,这里不是取不到,而是必须用 localstoreage 来赋值,因为 vuex 只是一个分发管理状态的作用,并没有本地保存的功能,

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

import store from "../store";
import router from "../router.js"; // 配置API接口地址
var root = "http://localhost:58427/api";
var root1 = "http://apk.neters.club/api";
// 引用axios
var axios = require("axios");
// 自定义判断元素类型JS
function toType(obj) {
return {}.toString
.call(obj)
.match(/\s([a-zA-Z]+)/)[]
.toLowerCase();
}
// 参数过滤函数
function filterNull(o) {
for (var key in o) {
if (o[key] === null) {
delete o[key];
}
if (toType(o[key]) === "string") {
o[key] = o[key].trim();
} else if (toType(o[key]) === "object") {
o[key] = filterNull(o[key]);
} else if (toType(o[key]) === "array") {
o[key] = filterNull(o[key]);
}
}
return o;
} // http request 拦截器
axios.interceptors.request.use(
config => {
if (window.localStorage.Token&&window.localStorage.Token.length>=128) {//store.state.token 获取不到值??
// 判断是否存在token,如果存在的话,则每个http header都加上token
config.headers.Authorization ="Bearer "+ window.localStorage.Token;
}
return config;
},
err => {
return Promise.reject(err);
}
);
// http response 拦截器
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case :
// 返回 401 清除token信息并跳转到登录页面
router.replace({
path: "login",
query: { redirect: router.currentRoute.fullPath }
});
}
}
return Promise.reject(error.response.data); // 返回接口返回的错误信息
}
);
/*
接口处理函数
这个函数每个项目都是不一样的,我现在调整的是适用于
https://cnodejs.org/api/v1 的接口,如果是其他接口
需要根据接口的参数进行调整。参考说明文档地址:
https://cnodejs.org/topic/5378720ed6e2d16149fa16bd
主要是,不同的接口的成功标识和失败提示是不一致的。
另外,不同的项目的处理方法也是不一致的,这里出错就是简单的alert
*/ function apiAxios(method, url, params, success, failure) {
if (params) {
params = filterNull(params);
}
axios({
method: method,
url: url,
data: method === "POST" || method === "PUT" ? params : null,
params: method === "GET" || method === "DELETE" ? params : null,
baseURL: root,
withCredentials: false
})
.then(function(res) {
success(res.data);
})
.catch(function(err) {
let res = err.response;
if (err) {
window.alert("api error, HTTP CODE: " + res.status);
}
});
} // 返回在vue模板中的调用接口
export default {
get: function(url, params, success, failure) {
return apiAxios("GET", url, params, success, failure);
},
post: function(url, params, success, failure) {
return apiAxios("POST", url, params, success, failure);
},
put: function(url, params, success, failure) {
return apiAxios("PUT", url, params, success, failure);
},
delete: function(url, params, success, failure) {
return apiAxios("DELETE", url, params, success, failure);
}
};

运行项目查看:

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录

大家观察可以看到,我们第一次点击 Home 的时候,发现跳转到了 Login 页面,然后登录后,自动跳转首页,并成功获取到数据,登录成功!

然后退出登录,发现首页已经进不去了,退出成功!

四、说明

今天因为时间的关系,没有把 Vuex 在路由中如何获取研究出来,这里先用了本地缓存来代替了,大家如果有知道的小伙伴,请留言哈~~~不胜感激,

五、CODE

前端:
https://github.com/anjoy8/Blog.Vue

后端:

https://github.com/anjoy8/Blog.Core

从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十四║ Vuex + JWT 实现授权验证登录的更多相关文章

  1. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 二十五&boxV;初探SSR服务端渲染(个人博客二)

    缘起 时间真快,现在已经是这个系列教程的下半部 Vue 第 12 篇了,昨天我也简单思考了下,可能明天再来一篇,Vue 就基本告一段落了,因为什么呢,这里给大家说个题外话,当时写博文的时候,只是想给大 ...

  2. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 二十&boxV;Vue基础终篇:传值&plus;组件&plus;项目说明

    缘起 新的一天又开始啦,大家也应该看到我的标题了,是滴,Vue基础基本就到这里了,咱们回头看看这一路,如果你都看了,并且都会写了,那么现在你就可以自己写一个Demo了,如果再了解一点路由,ajax请求 ...

  3. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 二十二&boxV;Vue实战:个人博客第一版&lpar;axios&plus;router&rpar;

    前言 今天正式开始写代码了,之前铺垫了很多了,包括 6 篇基础文章,一篇正式环境搭建,就是为了今天做准备,想温习的小伙伴可以再看看<Vue 基础入门+详细的环境搭建>,内容很多,这里就暂时 ...

  4. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 二十三&boxV;Vue实战:Vuex 其实很简单

    前言 哈喽大家周五好,马上又是一个周末了,下周就是中秋了,下下周就是国庆啦,这里先祝福大家一个比一个假日嗨皮啦~~转眼我们的专题已经写了第 23 篇了,好几次都坚持不下去想要中断,不过每当看到群里的交 ...

  5. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 二十一&boxV;Vue实战:开发环境搭建【详细版】

    缘起 哈喽大家好,兜兜转转终于来到了Vue实战环节,前边的 6 篇关于Vue基础文章我刚刚简单看了看,感觉写的还是不行呀,不是很系统,所以大家可能看上去比较累,还是得抽时间去润润色,修改修改语句和样式 ...

  6. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 十五 &boxV;Vue基础:JS面向对象&amp&semi;字面量&amp&semi; this字

    缘起 书接上文<从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十四 ║ VUE 计划书 & 我的前后端开发简史>,昨天咱们说到了以我的经历说明的web开发经历的 ...

  7. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 十六 &boxV;Vue基础:ES6初体验 &amp&semi; 模块化编程

    缘起 昨天说到了<从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十五 ║ Vue前篇:JS对象&字面量&this>,通过总体来看,好像大家对这一块不是很 ...

  8. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 十八&boxV;Vue基础&colon; 指令&lpar;下&rpar;&plus;计算属性&plus;watch

    回顾 今天来晚辣,给公司做了一个小项目,一个瀑布流+动态视频控制的DEMO,有需要的可以联系我,公司的项目就不对外展示了(一个后端程序员真的要干前端了哈哈哈). 书接上文,昨天正式的开始了Vue的代码 ...

  9. 从壹开始前后端分离 &lbrack; Vue2&period;0&plus;&period;NET Core2&period;1&rsqb; 十四 &boxV; VUE 计划书 &amp&semi; 我的前后端开发简史

    ---新内容开始--- 番外 大家周一好呀,又是元气满满的一个周一呀!感谢大家在周一这个着急改Bug的黄金时期,抽出时间来看我的博文哈哈哈,时间真快,已经到第十四篇博文了,也很顺顺(跌跌)利利 (撞撞 ...

随机推荐

  1. gulp操作基本功能&period;md

    gulp操作基本功能.示例代码: var gulp = require("gulp");//创建 gulp模块 var adel = require("del" ...

  2. &lbrack;Tools&rsqb; maven-eclipse安装及配置

    [背景] 买了个surface,带到公司当做开发机器来用,各种环境都需要重新安装,写个笔记记录下maven安装步骤,虽然很简单,但是我这脑子,容易忘记,写下来以备用 [开工] 安装Maven 访问 M ...

  3. storyBoard配置错误导致崩溃 superview&rsqb;&colon; unrecognized selector&period;&period;&period;

    控制台打印崩溃原因 [TaskStartVC superview]: unrecognized selector sent to instance RT TaskStartVC是一个同storyBoa ...

  4. div模拟表格使用display

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  5. 老周的ABP框架系列教程 -》 一、框架理论初步学习

    老周的ABP框架系列教程 -- 一.框架理论初步学习   1. ABP框架的来源与作用简介 1.1  简介 1.1.1       ABP框架全称为"ASP.NET Boilerplate ...

  6. AIX5&period;3CPU占用高的问题核查

    AIX5.3 CPU占用高问题核查步骤 1.topas查看占用cpu占用最高的进程的PID 2.执行: ps -mp PID -o THREAD 以查找相应进程下所有正在占用 CPU 的线程的TID ...

  7. Flask--特殊装饰器&comma; CBV&comma; 三方组件

    一. Flask中的特殊装饰器 before_request # before_request 是在视图函数执行之前执行的 @app.before_request def before(): prin ...

  8. 1709&colon; Fire or Retreat(zzuli)

    水题,哎,可是第一次是因为编译错了,vs不知咋了,无奈: 后面几次又因为类型用了int错了,痛苦: 题目描述 在与科技水平远胜于我们的外星人的战斗最后,我们能够用来对外星装甲造成伤害的武器只剩下了…… ...

  9. urls&period;py的配置&lbrack;路由配置&rsqb;

    urls.py的配置[路由配置] Get请求与Post请求的方式 get请求: (1)地址栏输入url (2)<a href="请求url">点击</a> ...

  10. PHP 中的魔法常数

    PHP中的魔法常数 PHP中有很多描述当前状态的魔法函数,可以很方便地获取运行时的局部环境 测试代码及结果如下 <?php namespace NS { function writeln($v= ...