赞
踩
前后端分离开发:将项目的视图显示和数据处理进行分离(分离开发、分离部署),目的是提升开发效率、降低对后端服务器的访问压力。
前后端分离开发技术:
文档:01-web前端技术体系.md
链接:http://note.youdao.com/noteshare?id=6c4f94c22e0d4ccca2636073f68f2542&sub=AD5058272EE5402295981FEFED39FDA3
文档:03-MVVM模式.md
链接:http://note.youdao.com/noteshare?id=8d812ecad7434aaf22e647125ad2bd0d&sub=3187F722F0FB4ACA8DC6CB8E5A5526DA
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
vue是一个基于MVVM思想的前端框架,主要用于完成数据的渲染
一个JS框架引入的方式有三种:
- npm安装使用
- 在线引用cdn
- 本地引入(js下载并存放到我们自己的web项目中,然后在页面使用)
在线CND
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
本地引入
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
</body>
<script type="text/javascript" src="js/vue.js" ></script>
</html>
案例
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> <div id="container"> <!--Mustache语法--> <p>{{str}}</p> <input type="text" v-model="str" /> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{ str:"千锋教育" } }); </script> </html>
每个 Vue 应用都是通过用
Vue
函数创建一个新的 Vue 实例开始的;vue实例实现了数据与视图(HTML标签)的绑定,因此对于vue实例而言必不可少的两个部分:
- html标签
- 数据:vue实例中的data指定渲染的数据,数据可以是各种类型,但必须满足JSON对象格式
插值:将vue实例中的数据(data)绑定到html模板中
文本:{{attrName}} – Mustache语法
标签属性绑定 v-bind
<input v-bind:type="inputType" v-bind:name="inputName"/>
原始HTML
<div v-html="htmlstr"></div>
支持JS表达式
<p>{{number+1}}</p>
<P>{{gender=='M'?'男':'女'}}</P>
<p>{{ message.split('').reverse().join('') }}</p>
<p>{{ 'http://'+message}}</p>
vue指令 v-
开头的表达式
指令
v-once 只挂载一次
v-bind 属性绑定数据
v-html 显示原始html
v-model 实现表单组件的双向绑定
<input type="text" v-model:value="message"/>
<input type="text" v-model="message"/>
v-on 绑定事件 触发执行vue实例中methods选项定义的js函数
参数
动态参数
<input type="text" v-bind:[aaa]="inputName"/>
缩写
v-bind:
----> :
v-on:
---->@
计算属性:一个属性的值是通过对现有属性进行计算得到,现有属性值的改变会触发这个属性值的改变
<body > <div id="container"> str1:<input v-model="str1"/><br /> str2:<input v-model="str2"/><br /> <p>{{str3}}</p> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{ str1:"千锋", str2:"武汉" }, computed:{ //相当于‘get方法’ 需要有返回值 str3:function(){ return this.str1+this.str2; } } }); </script>
侦听器:监听的属性值发生变化就会触发函数的执行
<body > <div id="container"> str1:<input v-model="str1"/><br /> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{ str1:"千锋" }, watch:{ str1:function(val){ console.log("str1:---"+val); } } }); </script>
CSS绑定
<head> <meta charset="utf-8" /> <title></title> <style type="text/css"> .style1{color:red;} .style2{color:blue;} .style3{border:1px deepskyblue solid;border-radius: 5px;} </style> </head> <body > <div id="container"> <div :class="{'style1':isShowColor,'style3':isShowBorder}" style="width: 100px; height: 100px;"> {{str}} </div> <div :class="[s1,s3]" style="width: 100px; height: 100px;">{{str}}</div> <div :class="[isRed?s1:s2,s3]" style="width: 100px; height: 100px;">{{str}}</div> <div :class="[isShowColor?s1:'',s3]" style="width: 100px; height: 100px;"> {{str}} </div> <div :class="[{'style1':isShowColor},s3]" style="width: 100px; height: 100px;">{{str}}</div> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{ str:"千锋教育", s1:"style1", s2:"style2", s3:"style3", isShowColor:true, isShowBorder:true, isRed:false } }); </script>
条件渲染
列表渲染
vue引用中事件触发的函数定义在vue实例的method选项中
使用 v-on
|@
实现事件的监听
案例:
<body > <div id="container"> <input type="button" value="测试按钮1" @click="test1"/> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{}, methods:{ test1:function(){ alert(1); } } }); </script>
在js函数中定义event参数
案例:通过event对象实现传值
<body > <div id="container"> <input type="button" value="测试按钮1" :data-bid="bookId" data-haha="bb" @click="test1"/> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{ bookId:101 }, methods:{ test1:function(event){ //事件对象:触发函数执行的事件 console.log(event); console.log(event.srcElement.dataset.bid); } } }); </script>
案例:通过参数传值
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body > <div id="container"> <h1 v-if="awesome === 'M'">if --- Vue is awesome!</h1> <h1 v-show="awesome === 'M'">show --- Vue is awesome!</h1> <table style="width:100%; border: 1px;" border="1" cellspacing="0"> <tr> <th>序号</th> <th>图书编号</th> <th>名称</th> <th>作者</th> <th>价格</th> <th>操作</th> </tr> <tr v-for="book,index in books"> <td>{{index+1}}</td> <td>{{book.bookId}}</td> <td>{{book.bookName}}</td> <td>{{book.bookAuthor}}</td> <td>{{book.bookPrice}}</td> <td> <input type="button" value="删除" :data-bookid="book.bookId" @click="doDel"/> <input type="button" value="修改" @click="doUpdate(book.bookId,$event)"/> </td> </tr> </table> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{ awesome:"F", books:[ { bookId:1001, bookName:"Java", bookAuthor:"张三", bookPrice:22.22 }, { bookId:1002, bookName:"Python", bookAuthor:"张三", bookPrice:22.22 }, { bookId:1003, bookName:"HTML5", bookAuthor:"张三", bookPrice:22.22 }, { bookId:1004, bookName:"vue", bookAuthor:"张三", bookPrice:22.22 } ] }, methods:{ doDel:function(event){ var bid = event.srcElement.dataset.bookid; console.log(bid); }, doUpdate:function(bid,event){ console.log(event); console.log("修改--"+bid); } } }); </script> </html>
实例:
<body > <div id="container"> <input type="text" v-model="str" @keyup.enter="test"/> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{ str:"" }, methods:{ test:function(){ location.href="https://www.baidu.com/s?wd="+this.str; } } }); </script>
常见按键修饰符
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
输入组件与数据的双向绑定
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body > <div id="container"> <p>帐号:<input type="text" v-model="user.userName"/></p> <p>密码:<input type="password" v-model="user.userPwd"/></p> <p> 性别:<input type="radio" v-model="user.userGender" value="M"/>男 <input type="radio" v-model="user.userGender" value="F"/>女 </p> <p> 爱好:<input type="checkbox" value="篮球" v-model="user.hobbies"/>篮球 <input type="checkbox" value="足球" v-model="user.hobbies"/>足球 <input type="checkbox" value="羽毛球" v-model="user.hobbies"/>羽毛球 <input type="checkbox" value="溜溜球" v-model="user.hobbies"/>溜溜球 </p> <p> 城市:<select v-model="user.city"> <option value="北京">北京</option> <option value="上海">上海</option> <option value="广州">广州</option> <option value="深圳">深圳</option> <option value="武汉">武汉</option> </select> </p> <p> 交友宣言: <textarea v-model="user.desc"></textarea> </p> <p> <input type="button" value="提交" @click="doSubmit"/> </p> </div> </body> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container", data:{ user:{ userName:"admin", userPwd:"123456", userGender:"M", hobbies:["足球","羽毛球"], city:"武汉", desc:"wahaha" } }, methods:{ doSubmit:function(){ console.log("----~~~~----"); console.log(vm.user); } } }); </script> </html>
接口名称 | 在线音乐搜索 |
---|---|
功能描述 | |
请求URL | http://47.96.11.185:9999/music/search |
请求方式 | GET | POST |
参数 | s string [必须] 搜索关键字 limit int [可选] 返回搜索结果的行数(默认10) type int [可选] 搜索类型(1 单曲,10 … ,100 …) offset int [可选] 从搜索结果第几条开始返回(默认0) |
返回结果 | code 查询结果状态 200 表示查询成功 result 查询结果 包含搜索的音节结果集和总条数 songs 音乐结果集 songCount 总条数 |
结果示例: |
组件,将通用的HTML\CSS\JS模块进行封装
好处:实现复用
Vue.component(“组件名称”,{组件模版});
mystyle.css
body{padding: 0px;margin: 0px;}
.navbarDiv{background: #009688;padding: 12px 25px;}
.navbarDiv .titleLabel{
color: white;font-family: "微软雅黑";font-size: 18px;font-weight: bold;
}
.navbarDiv button{
border: none;border-radius: 3px;background: #009688;
color: white;margin-left: 15px;padding: 5px 10px;
}
.navbarDiv button:hover{background: white;color: #009688;}
mycomponents.js
Vue.component("navbar",{ template:`<div class="navbarDiv"> <label class="titleLabel">千锋教育</label> <button @click="goHome">首页</button> <button @click="goJava">Java</button> <button @click="goPython">Python</button> <button @click="goHtml5">HTML5</button> <button @click="goAbout">关于我们</button> </div>`, methods:{ goHome:function (){ console.log("~~~goHome"); }, goJava:function (){ console.log("~~~goJava"); }, goPython:function (){ console.log("~~~goPython"); }, goHtml5:function (){ console.log("~~~goHtml5"); }, goAbout:function (){ console.log("~~~goAbout"); } } });
html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" href="css/mystyle.css" /> </head> <body> <div id="container"> <navbar></navbar> </div> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript" src="js/mycomponents.js" ></script> <script type="text/javascript"> var vm = new Vue({ el:"#container" }); </script> </body> </html>
在组件中的data中定义组件使用的数据,data的数据时通过函数返回的数据集
父与子:组件需要在vue实例中来使用,vue实例称为组件的父容器,组件则是vue实例的子组件
通过子组件定义props完成传值
通过子组件触发绑定在子组件上的自定义事件从而调用父组件中函数
在组件中使用slot定义一个插槽
Vue.component("navbar",{
template:`<div class="navbarDiv">
<slot></slot>
<button @click="goHome">首页</button>
<button @click="goJava">Java</button>
<button @click="goPython">Python</button>
<button @click="goHtml5">HTML5</button>
<button @click="goAbout">关于我们</button>
<button @click="childMethod">Go</button>
</div>`
});
在调用组件时,可以在自定义组件中添加子标签,子标签就会被插入的插槽的位置
<div id="container">
<navbar>
<label>千锋武汉</label>
<label>Java2002</label>
</navbar>
</div>
当组件中的插槽数量>1时,需要给插槽取名字,我们可以将组件中的HTML元素插入到指定的插槽
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="container"> <mybanner> <template slot="s1"> <label>从前有座山</label> <label>山里有座庙</label> </template> <template slot="s2"> <img src="img/logo.png" height="30"/> </template> </mybanner> </div> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> Vue.component("mybanner",{ template:`<div style="height:100px; background:deepskyblue"> <slot name="s1"></slot> <hr/> <slot name="s2"></slot> </div>` }); var vm = new Vue({ el:"#container", data:{ value1:3 } }); </script> </body> </html>
通过在模版中的slot-scope属性可以获取插槽上绑定的属性
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div id="container"> <mybanner> <template slot="s1" slot-scope="data"> <label>{{data.attr1}}</label> <label>{{data.attr2}}</label> </template> <template slot="s2"> <img src="img/logo.png" height="30"/> </template> </mybanner> </div> <script type="text/javascript" src="js/vue.js" ></script> <script type="text/javascript"> Vue.component("mybanner",{ template:`<div style="height:100px; background:deepskyblue"> <slot name="s1" v-bind:attr1="user" v-bind:attr2="book"></slot> <hr/> <slot name="s2"></slot> </div>`, data:function(){ return { user:{ id:1, name:"zhangsan" }, book:{ bid:101, bname:"Java" } }; } }); var vm = new Vue({ el:"#container", data:{ value1:3 } }); </script> </body> </html>
vue实现数据渲染,但是不具备与服务器的通信能力;在项目开发中我们需要通过ajax实现通信:
- 原生ajax 繁琐
- jQuery ajax 笨重
- axios 简洁、高效的异步通信,对RESTful的良好支持
axios 是对ajax的封装的js库(js文件),实现前后端之间的异步交互
在html中引入axios的js库
npm
cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
离线引用
简单使用案例
axios.get("url",{params:{s:"抖音热歌",limit:15}}).then(function(res){
//回调函数 res.data 是 响应的数据
}).catch(function(e){
//异常处理函数
});
//参数传递默认使用 body 传递,在控制器中需要通过@RequestBody接收数据
axios.post("url",{bookId:1001,bookName:"Java"}).then(function(res){
//
});
@RestController
@RequestMapping("/axios")
@CrossOrigin
public class AxiosController {
@RequestMapping("/get")
public Book getBook(@RequestBody Book book){
System.out.println("--------------get");
return new Book();
}
}
axios({ method: 'post', url: 'http://localhost:8080/axios/get', data: { bookId:101, bookName:"Java" }, params:{ s:"抖音热歌" }, headers:{ token:"AAAA" } }).then(function(res){ //回调函数 }).catch(function(e){ //异常处理 });
@RestController
@RequestMapping("/axios")
@CrossOrigin
public class AxiosController {
@RequestMapping("/get")
public Book getBook(@RequestBody Book book,
@RequestParam String s,
@RequestHeader String token){
System.out.println("--------------get");
return new Book();
}
}
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
}));
axios对RESTful URL设计风格有很好的支持
axios.get(url[, config]) 查询操作
axios.delete(url[, config]) 删除操作
axios.options(url[, config]) 浏览器向服务器发送请求预检
axios.post(url[, data[, config]]) 添加操作
axios.put(url[, data[, config]]) 修改操作
{ // `url` 是用于请求的服务器 URL url: '/user', // `method` 是创建请求时使用的方法 method: 'get', // default // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。 // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL baseURL: 'https://some-domain.com/api/', // `transformRequest` 允许在向服务器发送前,修改请求数据 // 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法 // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream transformRequest: [function (data, headers) { // 对 data 进行任意转换处理 return data; }], // `transformResponse` 在传递给 then/catch 前,允许修改响应数据 transformResponse: [function (data) { // 对 data 进行任意转换处理 return data; }], // `headers` 是即将被发送的自定义请求头 headers: {'X-Requested-With': 'XMLHttpRequest'}, // `params` 是即将与请求一起发送的 URL 参数 // 必须是一个无格式对象(plain object)或 URLSearchParams 对象 params: { ID: 12345 }, // `paramsSerializer` 是一个负责 `params` 序列化的函数 // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/) paramsSerializer: function(params) { return Qs.stringify(params, {arrayFormat: 'brackets'}) }, // `data` 是作为请求主体被发送的数据 // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH' // 在没有设置 `transformRequest` 时,必须是以下类型之一: // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams // - 浏览器专属:FormData, File, Blob // - Node 专属: Stream data: { firstName: 'Fred' }, // `timeout` 指定请求超时的毫秒数(0 表示无超时时间) // 如果请求话费了超过 `timeout` 的时间,请求将被中断 timeout: 1000, // `withCredentials` 表示跨域请求时是否需要使用凭证 withCredentials: false, // default // `adapter` 允许自定义处理请求,以使测试更轻松 // 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)). adapter: function (config) { /* ... */ }, // `auth` 表示应该使用 HTTP 基础验证,并提供凭据 // 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头 auth: { username: 'janedoe', password: 's00pers3cret' }, // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' responseType: 'json', // default // `responseEncoding` indicates encoding to use for decoding responses // Note: Ignored for `responseType` of 'stream' or client-side requests responseEncoding: 'utf8', // default // `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称 xsrfCookieName: 'XSRF-TOKEN', // default // `xsrfHeaderName` is the name of the http header that carries the xsrf token value xsrfHeaderName: 'X-XSRF-TOKEN', // default // `onUploadProgress` 允许为上传处理进度事件 onUploadProgress: function (progressEvent) { // Do whatever you want with the native progress event }, // `onDownloadProgress` 允许为下载处理进度事件 onDownloadProgress: function (progressEvent) { // 对原生进度事件的处理 }, // `maxContentLength` 定义允许的响应内容的最大尺寸 maxContentLength: 2000, // `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte validateStatus: function (status) { return status >= 200 && status < 300; // default }, // `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目 // 如果设置为0,将不会 follow 任何重定向 maxRedirects: 5, // default // `socketPath` defines a UNIX Socket to be used in node.js. // e.g. '/var/run/docker.sock' to send requests to the docker daemon. // Only either `socketPath` or `proxy` can be specified. // If both are specified, `socketPath` is used. socketPath: null, // default // `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项: // `keepAlive` 默认没有启用 httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), // 'proxy' 定义代理服务器的主机名称和端口 // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据 // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。 proxy: { host: '127.0.0.1', port: 9000, auth: { username: 'mikeymike', password: 'rapunz3l' } }, // `cancelToken` 指定用于取消请求的 cancel token // (查看后面的 Cancellation 这节了解更多) cancelToken: new CancelToken(function (cancel) { }) }
{ // `data` 由服务器提供的响应 data: {}, // `status` 来自服务器响应的 HTTP 状态码 status: 200, // `statusText` 来自服务器响应的 HTTP 状态信息 statusText: 'OK', // `headers` 服务器响应的头 headers: {}, // `config` 是为请求提供的配置信息 config: {}, // 'request' // `request` is the request that generated this response // It is the last ClientRequest instance in node.js (in redirects) // and an XMLHttpRequest instance the browser request: {} }
原始写法
axios.get("http://47.96.116.68:9999/music/search",{params:{s:'抖音热歌',limit:'15'}}).then(function(res){
console.log(res.data);
vm.musics = res.data.result.songs;
});
箭头函数
axios.get("http://47.96.116.68:9999/music/search",{params:{s:'抖音热歌',limit:'15'}}).then((res)=>{
console.log(res.data);
this.musics = res.data.result.songs;
});
Representational State Transfer,表述性状态传递
RESTful 就是遵守REST规则的url设计规范
每个请求的URL代表服务端的唯一资源
101 Java
102 C++
传统的URL设计(不符合REST规范,同一个url可以删除多个图书)
http://localhost:8080/book/delete?id=101
http://localhost:8080/book/delete?id=102
REST风格的URL设计
http://localhost:8080/book/delete/101
http://localhost:8080/book/delete/102
http://localhost:8080/book/get/101
http://localhost:8080/book/get/102
不同的操作使用不同的请求方式
删除操作
http://localhost:8080/book/101 [delete]
查询操作
http://localhost:8080/book/101 [get]
资源的表现形式是以JSON/XML形式
客户端与接口之间的交互式无状态的(HTTP协议)(session就不可用)
@RestController @RequestMapping("/book") public class BookController { @Resource private BookService bookService; @RequestMapping(value = "/list",method = RequestMethod.GET) public List<Book> listBook(){ System.out.println("----------------list"); return bookService.listBooks(); } // http://localhost:8080/book/101 @RequestMapping(value = "/{id}",method = RequestMethod.GET) public Book getBook(@PathVariable("id") int bookId){ System.out.println("----------------get"); return new Book(); } // http://localhost:8080/book/101 @RequestMapping(value = "/{id}",method = RequestMethod.DELETE) public ResultVO deleteBook(@PathVariable("id") int bookId){ System.out.println("----------------delete"); return new ResultVO(); } // http://localhost:8080/book/101 @RequestMapping(value = "/add",method = RequestMethod.POST) public ResultVO saveBook(@RequestBody Book book){ System.out.println("----------------add"); return new ResultVO(); } // http://localhost:8080/book/101 @RequestMapping(value = "/{id}",method = RequestMethod.PUT) public ResultVO updateBook(@RequestBody Book book){ System.out.println("----------------update"); return new ResultVO(); } }
swagger2是一个用于生成服务器接口说明规范性文档、同时能够完成对接口的测试的工具(框架)
添加依赖
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
配置
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket getDocket(){ //Docket对象用于设置接口说明信息 Docket docket = new Docket(DocumentationType.SWAGGER_2) .apiInfo(getApiInfo()) //设置说明文档的“封面”信息 .select() .apis(RequestHandlerSelectors.basePackage("com.qfedu.springboot.demo2.controllers")) //指定扫描接口的范围 .paths(PathSelectors.any()) .build(); return docket; } public ApiInfo getApiInfo(){ ApiInfoBuilder builder = new ApiInfoBuilder() .title("微分销管理平台接口说明文档") //设置标题 .description("这是api接口说明文档的描述信息") .version("2.0.1") .contact(new Contact("阿龙","http://www.qfedu.com","haha@hehe.com")); ApiInfo apiInfo = builder.build(); return apiInfo; } }
启动应用,访问api文档
为了生成更为规范的api文档,swagger提供了响应的注解
@Api
类注解,说明当前控制器类的作用
@RestController
@RequestMapping("/book")
@Api(tags = "图书信息接口")
public class BookController {
}
@ApiOperation
方法注解,对接口的功能进行描述
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
@ApiOperation(value = "根据ID查询图书信息", notes = "辅助说明")
public Book getBook(@PathVariable("id") int bookId){
System.out.println("----------------get");
return new Book();
}
@ApiImplicitParam
方法注解,用于对接口的参数进行说明
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
@ApiOperation(value = "根据ID查询图书信息", notes = "辅助说明")
@ApiImplicitParam(name = "id", value = "要查询的图书的id",required = true, dataType = "int")
public Book getBook(@PathVariable("id") int bookId){
System.out.println("----------------get");
return new Book();
}
@ApiImplicitParams
方法注解,用于多参数接口的参数说明
@RequestMapping(value = "/get",method = RequestMethod.GET)
@ApiImplicitParams({
@ApiImplicitParam(name = "bookId", value = "要查询的图书的id",required = true, dataType = "int"),
@ApiImplicitParam(name = "bookName", value = "要查询的图书的名称",required = true, dataType = "string")
})
public Book getBook(int bookId,String bookName){
System.out.println("----------------get111");
return new Book();
}
@ApiModel
类注解
@ApiModelProperty
属性注解
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@ApiModel(value = "图书对象",description = "包含图书信息的实体对象")
public class Book {
@ApiModelProperty(value = "图书ID",dataType = "int",required = true)
private int bookId;
@ApiModelProperty(value = "图书名称",dataType = "string",required = true)
private String bookName;
private String bookAuthor;
private double bookPrice;
private String bookImg;
}
@ApiIgnore
方法注解,用在接口方法前,用于声明忽略此接口的说明
前后端分离开发,前端和服务端之间是无状态请求,每次请求都会创建一个新的session,因此不能使用session来实现用户认证。
使用redis构建一个“公共session”
Json Web Token 就是一种token字符串的生成规则
导入jwt依赖
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
<scope>runtime</scope>
</dependency>
生成token
//生成token
String token = Jwts.builder()
.setSubject(userName) //设置用户信息
.setId("101") //设置用户ID
.setIssuedAt(new Date()) //设置token的创建时间
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60000)) //设置过期时间
.signWith(SignatureAlgorithm.HS256, "QFedu123")
.compact();
认证token
package com.qfedu.jwtserver.controller; import com.qfedu.jwtserver.beans.Goods; import com.qfedu.jwtserver.vo.ResultVO; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.JwtParser; import io.jsonwebtoken.Jwts; import org.springframework.web.bind.annotation.*; import javax.xml.transform.Result; import java.util.*; @RestController @RequestMapping("/goods") @CrossOrigin public class GoodsController { @RequestMapping(value = "/list",method = RequestMethod.GET) public ResultVO listGoods(@RequestHeader(required = false) String token){ if (token == null){ return new ResultVO(1,"请先登录!"); }else{ //验证token try { JwtParser parser = Jwts.parser(); parser.setSigningKey("QFedu123"); Jws<Claims> jws = parser.parseClaimsJws(token); //获取的解析的token中的用户名 String username = jws.getBody().getSubject(); ArrayList<Goods> goodsList = new ArrayList<>(); goodsList.add(new Goods("10001", "Java")); goodsList.add(new Goods("10002", "C++")); goodsList.add(new Goods("10003", "Python")); return new ResultVO(0,"success",goodsList); }catch (Exception e){ return new ResultVO(1,"请重新登录!"); } } } }
`java
package com.qfedu.jwtserver.controller;
import com.qfedu.jwtserver.beans.Goods;
import com.qfedu.jwtserver.vo.ResultVO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import org.springframework.web.bind.annotation.*;
import javax.xml.transform.Result;
import java.util.*;
@RestController
@RequestMapping("/goods")
@CrossOrigin
public class GoodsController {
@RequestMapping(value = "/list",method = RequestMethod.GET) public ResultVO listGoods(@RequestHeader(required = false) String token){ if (token == null){ return new ResultVO(1,"请先登录!"); }else{ //验证token try { JwtParser parser = Jwts.parser(); parser.setSigningKey("QFedu123"); Jws<Claims> jws = parser.parseClaimsJws(token); //获取的解析的token中的用户名 String username = jws.getBody().getSubject(); ArrayList<Goods> goodsList = new ArrayList<>(); goodsList.add(new Goods("10001", "Java")); goodsList.add(new Goods("10002", "C++")); goodsList.add(new Goods("10003", "Python")); return new ResultVO(0,"success",goodsList); }catch (Exception e){ return new ResultVO(1,"请重新登录!"); } } }
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。