一、登录功能
项目:white_jotter_demo + wj_demo_vue
http://localhost:8081/#/login
前端页面开发:
1、src\components 编写Login.vue,AppIndex.vue 登录页面、首页页面
2、设置反向代理
src\main.js中设置 axios
前端请求默认发送到url,全局注册,之后可在其他组件中通过 this.$axios 发送数据
3、配置页面路由
src\router\index.js 导入组件
4、设置跨域支持
让后端能够访问到前端的资源
config\index.js 中,找到 proxyTable 位置
后端开发:
1、User类
接收前端发送的数据,一个js对象
2、Result类
Result 类,一个响应码,是为了构造 response
Login()方法 返回对象的类型
3、LoginController
对响应进行处理,业务逻辑,
整个流程:
前端 Login.vue 中,发送post请求,通过设置 发到了 http://localhost:8443/api/login
.post('/login', {
username: this.loginForm.username,
password: this.loginForm.password
})
后端LoginController接收,@PostMapping(value = "api/login")
注释还有@ResponseBody,@CrossOrigin,共三个
LoginController里有一个方法:public Result login(@RequestBody User requestUser)
接收request,返回 Result类对象
前端 接收后端的response
.then(successResponse => {
if (successResponse.data.code === 200) {
this.$router.replace({path: '/index'})
}
})
响应码一致,登录成功跳转
综上所述
整个登录过程:
前端:Login.vue, AppIndex.vue, router/index.js
配置文件:Main.js 配置了 反向代理axios,Config/index.js 配置了proxyTable
后端:User.java, Result.java, LoginController.java
配置文件: application.properties 配置 server.port=8443
二、数据库验证登录功能
1、建表
2、后端代码
1、配置数据库:
pom.xml,配置依赖;application.properties 配置数据库
2、User类:
建立对数据库的映射
(1)添加User注释:
@Entity
@Table(name = "user")
@JsonIgnoreProperties({"handler","hibernateLazyInitializer"})
【备注】@Entity 表示这是一个实体类;@Table(name=“user”) 表示对应的表名是 user;
为了简化对数据库的操作,我们使用了 Java Persistence API(JPA),@JsonIgnoreProperties({ “handler”,“hibernateLazyInitializer” })
因为是做前后端分离,而前后端数据交互用的是 json 格式。 那么 User 对象就会被转换为 json 数据。 而本项目使用 JPA 来做实体类的持久化,JPA 默认会使用 hibernate, 在 JPA 工作过程中,就会创造代理类来继承 User ,并添加 handler 和 hibernateLazyInitializer 这两个无须 json 化的属性,所以这里需要用 JsonIgnoreProperties 把这两个属性忽略掉。
(2)添加主键id注释:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
@id和@GeneratedValue都是JPA的标准用法; IDENTITY:主键由数据库自动生成(主要是自动增长型)
3、UserDAO
Data Access Object(数据访问对象,DAO)即用来操作数据库的对象。这里我们通过继承 JpaRepository
的方式构建 DAO。
public interface UserDAO extends JpaRepository<User,Integer> {
User findByUsername(String username);
User getByUsernameAndPassword(String username,String password);
}
方法名是关键,由于使用了 JPA,无需手动构建 SQL 语句,而只需要按照规范提供方法的名字即可实现对数据库的增删改查。
两个方法,一个是通过用户名查询,一个是通过用户名及密码查询。
4、UserService
@Service
public class UserService {
@Autowired
UserDAO userDAO;
public boolean isExist(String username) {
User user = getByName(username);
return null!=user;
}
public User getByName(String username) {
return userDAO.findByUsername(username);
}
public User get(String username, String password){
return userDAO.getByUsernameAndPassword(username, password);
}
UserService对 UserDAO
进行了二次封装,一般来讲,我们在 DAO 中只定义基础的增删改查操作,而具体的操作,需要由 Service 来完成。
当然,由于我们做的操作原本就比较简单,所以这里看起来只是简单地重命名了一下,比如把 “通过用户名及密码查询并获得对象” 这种方法命名为 get
。
5、LoginController
添加一个属性:
@Autowired
UserService userService;
login方法里修改判断:
User user = userService.get(username, requestUser.getPassword());
if (null == user) {
return new Result(400);
} else {
return new Result(200);
}
这种简单的三层架构(DAO + Service + Controller):
- DAO 用于与数据库的直接交互,定义基础的增删改查操作;
- Service 负责业务逻辑,跟功能相关的代码一般写在这里,编写、调用各种方法对 DAO 取得的数据进行操作;
- Controller 负责数据交互,即接收前端发送的数据,通过调用 Service 获得处理后的数据并返回前端。
倾向于让 Controller 显得清凉一些
综上所述:建表、数据库配置文件、
4个类:User,UserDAO,UserService,LoginController
三、使用 Element前端开发
1、装element: cnpm i element-ui –S
2、引入element:
完整引入、在main.js中添加三行代码
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
修改 Login.vue
,把<template>里 最外层的 <div>
标签改为 <el-card>
3、使用Form组件
Element 的官方地址 https://element.eleme.cn/#/zh-CN/component/form
设计界面,修改 <template>
标签内的 html 和 <style>
标签内的 css
显示代码,复制 自己构建
login.vue中:
<el-form>
里面可以放置 <el-form-item>,然后
里面再放置其它的内容,比如 <el-input>,<el-button>
添加css样式
设置背景
在 <el-form>
标签的外又添加了一个父标签 <body>
,id 设置为 poster
,然后在 <style>
中添加内容
写一个 body 的样式,是为了覆盖掉浏览器(用户代理)的默认样式。
over
基本上 到这 就可以理解 项目的构成了。之后就是 业务功能 了。
基本模式:前端开发组件、后端开发控制器,调试功能。
四、登录拦截器与前端路由
这个登录页面其实没有用,别人直接输入首页的网址,就可以绕过登录页面。因此,我们还需要开发一个拦截器。
主要包含以下内容: 一、前端路由的 hash 模式与 history 模式;二、history 模式下后端错误页面的配置;三、登录拦截的实现
1、前端路由
URL 里有一个 #
号,#
被称为“锚点”,改变 URL 却不请求后端
利用 AJAX,我们可以不重载页面就刷新数据 + #
号的特性 = 可以在前端实现页面的整体变化,而不用每次都去请求后端
为了实现前端路由,我们可以监听 #
号后面内容的变化(hashChange
),从而动态改变页面内容。URL 的 #
号后面的地址被称为 hash
。这种实现方式我们称之为 Hash
模式,是非常典型的前端路由方式。
另一种常用的方式被称为 History
模式,这种方式使用了 History API
,History API
顾名思义就是针对历史记录的 API ,这种模式的原理是先把页面的状态保存到一个对象(state
)里,当页面的 URL 变化时找到对应的对象,从而还原这个页面。(其实原本人家这个功能是为了方便浏览器前进后退的,不得不说程序员们的脑洞真大。使用了这种模式,就可以摆脱 #
号实现前端路由。)
2、使用 History 模式
router\index.js 中,加入 mode: 'history',
运行项目,访问不加 #
号的 http://localhost:8080/login ,成功加载页面。
3、后端登录拦截器
一个简单的登录拦截器的逻辑如下:
1.用户访问 URL,检测是否为登录页面,如果是登录页面则不拦截
2.如果用户访问的不是登录页面,检测用户是否已登录,如果未登录则跳转到登录页面
1、把前端打包后部署在后端
变成了前后端不分离项目了。
npm run build
将前端项目的 dist目录下生成的static
文件夹和 index.html
文件,复制到我们后端项目的 wj\src\main\resources\static
文件夹下。
2、ErrorConfig类
package 名 error下,
实现 ErrorPageRegistrar
接口
把默认的错误页面设置为 /index.html
@Component
public class ErrorConfig implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry registry) {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
registry.addErrorPages(error404Page);
}
}
重新启动项目,访问 http://localhost:8443/login ,可以 成功进入登录页面。
3、LoginController
为了保存登录状态,我们可以把用户信息存在 Session
对象中(当用户在应用程序的 Web 页之间跳转时,存储在 Session
对象中的变量不会丢失)。这样在访问别的页面时,可以通过判断是否存在用户变量来判断用户是否登录。
在 LoginController 中,方法参数多传了一个值 HttpSession session,登录成功处添加了一条代码
session.setAttribute("user", user);
4、LoginInterceptor
package 名interceptor下,
继承 HandlerInterceptor接口,重写实现 preHandle
方法。
这里的逻辑就是:判断request.getServletPath()
是否是否是"/index"
,如果是,就判断是否登录(session.getAttribute("user")
是否为空),没有登录就重定向到"/login"
(response.sendRedirect(request.getContextPath() + "/login"
);
request_xxPath示意图
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
String servletPath = request.getServletPath();
if(servletPath.startsWith("/index")){
if(session.getAttribute("user") == null){
System.out.println("Not Login!");
response.sendRedirect(request.getContextPath() + "/login");
return false;
}
}
return true;
}
}
5、MyWebConfigurer
package 名 config下,继承
WebMvcConfigurer
@SpringBootConfiguration
public class MyWebConfigurer implements WebMvcConfigurer {
@Bean
public LoginInterceptor getLoginIntercepter(){
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getLoginIntercepter()).addPathPatterns("/**").excludePathPatterns("/index.html");
}
}
将写完了的拦截器,把它配置到项目中,最长的那条语句的作用 是对所有路径应用拦截器,除了 /index.html。
目前效果:
运行后端项目,访问 http://localhost:8443/index ,发现页面自动跳转到了 http://localhost:8443/login ,输入用户名和密码登录,跳转到 http://localhost:8443/index , 这时可以把浏览器标签关掉,再在一个新标签页输入 http://localhost:8443/index ,发现不会被拦截。
综上所述:
后端拦截器 步骤:
把前端打包后部署在后端、ErrorConfig类
LoginController、LoginInterceptor、MyWebConfigurer
问题:
现在是通过后端服务器的端口访问页面的,怎么样才能改成前端端口访问拦截?
他现在后端登录拦截器的作用:就是通过后端端口验证的,也就是就是在后端生效,而且这就是 前后端不分离项目了
难道后端拦截器 不能应用在 前后端分离项目 中吗? token给出了答案???
详情见 ,
对于前后端不分离项目 题主提出了 采用 Vuex 与前端登录拦截器
第十四部分 不知道是否可做出解释。
五、导航栏与图书管理页面
做 图书管理页面的前端部分
1、导航栏的实现
1、路由配置
App.vue
是所有组件的父组件。公共部分
新建Home.vue,components目录下,<template>中写入了一句 <router-view/>
,也就是子页面(组件)显示的地方。
在一个组件中通过导入引用了其它组件,也可以称之为父子组件。但想要通过 <router-view/>
控制子组件的显示,则需要进行路由的相关配置。
修改router/index.js, routes里面: 将index配置为home的子组件。
routes: [
{
path: '/home',
name: 'Home',
component: Home,
redirect: '/index',
children: [
{
path: '/index',
name: 'AppIndex',
component: AppIndex,
meta: {
requireAuth: true
}
}
]
}
]
注意我们并没有把首页的访问路径设置为 /home/index
,仍然可以通过 /index
访问首页,这样配置其实是感受不到 /home
这个路径的存在的。之后再添加新的页面,可以直接在 children
中增添对应的内容。
2.使用 NavMenu 组件
在 components
文件夹里新建一个 common
文件夹,用来存储公共的组件,并在该文件夹新建一个组件 NavMenu.vue
<el-menu>里面添加
<el-menu-item v-for="(item,i) in navList" :key="i" :index="item.name"> {{ item.navItem }} </el-menu-item>
(1)在 <el-menu>
标签中我们开启了 router
模式 ,启用该模式时会在 激活导航时 以index 作为path 进行路由跳转
(2)通过 v-for
指令,把 navList
数组渲染为一组 <el-menu-item>
元素,也即导航栏的内容。
然后,把NavMenu组件放在 Home.vue
中
<template>
<div>
<nav-menu></nav-menu>
<router-view/>
</div>
</template>
<script>
import NavMenu from './common/NavMenu'
export default {
name: 'Home',
components:{NavMenu}
}
</script>
2、图书管理页面
核心页面,我们先把它设计出来,以后再去实现具体的功能。
图书展示区域,分类导航栏,搜索栏,页码
1.LibraryIndex.vue
图书页面的根组件
使用了 Element 提供的 Container 布局容器,把整个页面分为了侧边栏和主要区域两个部分
2、配置这个页面的路由,修改 router/index.js
把他放到home 的children: 里
2.SideMenu.vue
编写一个侧边栏组件SideMenu.vue,放在 /library
目录下
在 LibraryIndex.vue
中使用这个组件
<SideMenu></SideMenu>, components:{SideMenu}
3.Books.vue
v-for
指令,之后可以使用动态渲染
el-tooltip
Element 组件,用于展示鼠标悬停时的提示信息。
slot
插槽,及把标签中的内容插到父组件指定的地方,这里我们插入了 el-tooltip
的 content
中。上述文档中亦有描述。
封面图像标签中,我们使用了 :src="item.cover"
这种写法,:
其实是 v-bind:
的缩写,用于绑定把标签的属性与 data 中的值绑定起来。
搜索栏暂不理会
分页使用 el-pagination
组件,目前只是样式。
把 Books 组件放在 LibraryIndex.vue
中
<books class="books-area"></books>
访问 http://localhost:8081/library ,效果图预览
综上所述:
设置顶部导航栏
1、Home.vue,配置router/index.js; 2、NavMenu.vue,把NavMenu组件放在Home.vue 中
编写图书馆页面,设置侧边栏,放书
1、
LibraryIndex.vue,配置router/index.js ,把他放到home 的children: 里; 2、SideMenu.vue,把其放入 LibraryIndex.vue中; 3、Books.vue,把其放入 LibraryIndex.vue中
六、增删改查
查询里涉及按关键字查询(图书检索),上传书籍信息里涉及图片上传
1、pojo层
新建两个 pojo,分别是Category 和 Book
Book中:
@ManyToOne
@JoinColumn(name = "cid")
private Category category;
把 category 对象的 id 属性作为 cid
2、dao层
新建一个 CategoryDAO ,一个 BookDAO
BookDAO 里两个方法 :findAllByCategory、findAllByTitleLikeOrAuthorLike
JPA 提供默认方法
3、Service 层
CategoryService: 方法 : List<Category> list()、 Category get(int id)
BookService: 方法:List<Book> list()、void addOrUpdate(Book book)、void deleteById(int id)、List<Book> listByCategory(int cid)
四个功能: 分别是查出所有书籍、增加或更新书籍、通过 id 删除书籍和通过分类查出书籍
4、Controller层
GetMapping 与 PostMapping :
HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的 查,改,增,删4个操作
GET一般用于获取/查询资源信息,而POST一般用于更新资源信息
换言之,查 get,增删改 post
PutMapping 与 DeleteMapping 这俩不咋用
get请求:a. 直接在浏览器地址栏输入某个地址;b. 点击链接;c. 表单默认的提交方式
Post请求: 设置表单method = “post”
LibraryController:
@RestController
public class LibraryController {
@Autowired
BookService bookService;
@GetMapping("/api/books")
public List<Book> list() throws Exception{
return bookService.list();
}
@PostMapping("/api/books")
public Book addOrUpdata(@RequestBody Book book) throws Exception{
bookService.addOrUpdate(book);
return book;
}
@PostMapping("/api/delete")
public void delete(@RequestBody Book book) throws Exception{
bookService.deleteById(book.getId());
}
@GetMapping("/api/categories/{cid}/books")
public List<Book> listByCategory(@PathVariable("cid") int cid) throws Exception{
if(cid != 0){
return bookService.listBycategory(cid);
}else {
return list();
}
}
测试:查询所有书籍,访问 http://localhost:8443/api/books;
测试分类,访问 http://localhost:8443/api/categories/1/books
综上所述:
pojo层、dao层、Service 层、Controller层
查:LibraryController list()方法,调用 bookService.list()方法, 其调用 bookDAO.findAll 方法,(JPA 默认提供的方法)
改:addOrUpdata() 方法,调用 bookService.addOrUpdate(book), 在调用 bookDAO.save(book) (JPA 默认提供的方法)
删:delete()方法,调用 bookService.deleteById(), 在调用 bookDAO.deleteById(id) (JPA 默认提供的方法)
按类别查: listByCategory方法, 调用 bookService.listBycategory(cid), bookDAO.findAllByCategory(category),
bookdao中自定义的 public List<Book> findAllByCategory(Category category),也没写具体的实现逻辑。
七、增删改查功能的前端实现
发送请求调用后端编写好的接口,再根据返回的结果动态渲染页面。
1.EditForm.vue(新增)
该组件增加或者修改图书的弹出表单。 library 文件夹下
<el-dialog> <el-form> <el-form-item> <el-button>
两个方法:clear ()、onSubmit ()
2.SearchBar.vue(新增)
该组件搜索的搜索框
<el-input> <el-button>
方法:searchClick ()
3.Books.vue(修改)
图书管理页面的核心组件,添加搜索框,添加增加、删除按钮,完善分页功能,构造增、删、改、查对应的请求
<template>里:
<search-bar @onSearch="searchResult" ref="searchBar"></search-bar>
<el-tooltip v-for="item in books.slice((currentPage-1)*pagesize,currentPage*pagesize)">
<edit-form @onSubmit="loadBooks()" ref="edit"></edit-form>
<el-pagination>
<script>里:
components:{EditForm, SearchBar},
4.LibraryIndex.vue(修改)
修改实现按分类查询
5.SideMenu.vue(修改)
实现了点击分类引发查询事件
1>查询功能:
- 打开页面,默认查询出所有图书并显示(即页面的初始化)
- 点击左侧分类栏,按照分类显示图书
- 在搜索栏中输入作者或书名,可以模糊查询出相关书籍
1、页面初始化
在打开页面时就自动触发相应代码发送请求并渲染页面
Vue 的 钩子函数 —— mounted
mounted 即 “已挂载” ,所谓挂载,就是我们写的 Vue 代码被转换为 HTML 并替换相应的 DOM树的 这个过程;这个过程完事儿的时候,就会执行 mounted 里面的代码
books.vue: <script>里, loadBooks方法写在methods里
mounted: function () {
this.loadBooks()
},
loadBooks () {
var _this = this
this.$axios.get('/books').then(resp => {
if (resp && resp.status === 200) {
_this.books = resp.data
}
})
}
利用 axios 发送了一个 get 请求,在接受到后端返回的成功代码后把 data
里的数据替换为后端返回的数据。利用 data
和 template
里相应元素的双向绑定,实现页面的动态渲染。
关于mounted的描述,el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。 main.js
里 new了一个vue对象,就是 vm.$el,id为app,在index.html是一个div,
2、分类查询
listByCategory方法写在LibraryIndex.vue中的methods里,
点击左侧导航栏,向后端发送一个带有参数的 get 请求,然后同样是修改 data
里的数据以实现动态渲染
listByCategory () {
var _this = this
var cid = this.$refs.sideMenu.cid
var url = 'categories/' + cid + '/books'
this.$axios.get(url).then(resp => {
if (resp && resp.status === 200) {
_this.$refs.booksArea.books = resp.data
}
})
}
组件之间的通信
在 LibraryIndex
组件的方法里,我们需要获取 SideMenu
组件的 data cid
在LibraryIndex中,给SideMenu组件,
ref="sideMenu"给 <SideMenu> 设置引用名。
<SideMenu @indexSelect="listByCategory" ref="sideMenu"></SideMenu>
这样,我们就可以通过 _this.refs.sideMenu
来引用侧面导航栏的实例,并获取它的数据 cid 了。
另外,前一句代码是 为 listByCategory()
方法设置了触发自定义事件 indexSelect。
在SideMenu.vue中,<script>里定义了一个方法:
methods: {
handleSelect (key, keyPath) {
this.cid = key
this.$emit('indexSelect')
}
}
emit,即触发,在子组件中使用 $emit 方法,即可触发在父组件中定义的事件。
handleSelect方法,在SideMenu.vue中,<template> <el-menu> 里,定义了 @select="handleSelect",即 由事件select 执行 handleSelect方法
总结:
点击选择 侧边导航栏 的一个标签后,
触发SideMenu.vue中 <template> <el-menu> 里的 select 事件,执行 handleSelect方法;
然后handleSelect方法 触发 父组件 LibraryIndex.vue 中
<template> <SideMenu>里的 indexSelect 事件,执行 listByCategory 方法;
同时 传参 将 SideMenu.vue中 <el-menu-item>
标签的 index
属性,赋值给 data 中定义的 cid;
前端利用axios 发送get请求,后端执行查询代码,返回数据 _this.$refs.booksArea.books = resp.data ,
注意 get() 括号里的url 与 后端 @GetMapping() 里的url 保持一致。
3、搜索栏查询
关于Controller 方法里的 传参数 注释 理解有下:
(1)@RequestBody 前端发请求 传过来的 对象,例如 @RequestBody Book book
(2)@PathVariable url路径里 带着的 信息,用这个 获得,例如 @PathVariable("cid") int cid
(3)@RequestParam 前端发请求 传过来的 参数 ,例如 @RequestParam("keywords") String keywords
bookservice里添加一个search方法,调用 bookDAO.findAllByTitleLikeOrAuthorLike(), librarycontroller里 search方法 调用 bookservice.search。
前端 核心的组件是 SearchBar
,核心的方法写在 Books.vue
(SearchBar
的父组件)里。
还是 父子组件调用、axios 请求 这一套。
SearchBar.vue里,
定义了 searchClick 方法, @keyup.enter.native="searchClick",@后的事件 执行了 该方法;
searchClick () {
this.$emit('onSearch')
}
然后因为这句,searchClick方法 又触发了 父组件的 onSearch事件;
Books.vue里,<search-bar @onSearch="searchResult" ref="searchBar"></search-bar>,根据这句
onSearch事件 又 执行了 searchResult 方法。
<template>里都是 事件执行方法,<script>里有 方法触发父组件的事件。
2>增加、修改、删除 功能:
前端 发送完请求后,在接收到后端返回的成功代码后,做法是:重新执行查询以显示修改后的数据;实现:执行查询对应的 Ajax 请求,利用双向绑定更新显示。
1、增加和修改
EditForm.vue、父组件 Books.vue
增加功能:
EditForm.vue中:
加号图标 <i @click="dialogFormVisible = true>
dialog 组件 <el-dialog> 弹出对话框,
visble.sync 属性 默认隐藏 ,点加号时 才会显示。
两个方法:
clear()
,在关闭输入框时清空原来的内容
onSubmit(),提交数据,并触发父组件Books.vue中定义的 onSubmit
事件,而这个事件对应的方法则是 loadBooks()
,即查询出所有的书籍。
修改功能:
Books.vue
中,给书籍封面上 添加一个事件 执行editBook方法,
<div class="cover" @click="editBook(item)">
<img :src="item.cover" alt="封面">
</div>editBook
这个方法即负责弹出修改表单并渲染数据:
2、删除
Books.vue中
安排一个图标元素的点击事件:
<i class="el-icon-delete" @click="deleteBook(item.id)"></i>
post 不能像 get 请求那样直接把参数写在 url 里,而需要以键值对的方式传递。
八、图片上传与项目的打包部署
1、图片上传
上传文件的逻辑:
前端向后端发送 post 请求,后端对接收到的数据进行处理(压缩、格式转换、重命名等),并保存到服务器中指定的位置,再把该位置对应的 URL 返回给前端即可。
前端部分:
写一个 ImgUpload.vue,他的父组件是EditForm.vue。
EditForm.vue
中更改:
import ImgUpload from './ImgUpload';
<img-upload @onUpload="uploadImg" ref="imgUpload"></img-upload> ;
添加一个method,uploadImg () { this.form.cover = this.$refs.imgUpload.url }
后端部分:
主要问题:
- 如何接收前端传来的图片数据并保存
- 如何避免重名(图片资源的名字很可能重复,如不修改可能出现问题)
现在想 前端上传一个图像,(不管该文件在本地哪里),可以在resources/img里存下来。
1)新建 utils
包,创建一个工具类 StringUtils
并编写生成指定长度随机字符串的方法
2)在 LibraryController
中添加 PostMapping,添加方法 public String coversUpload(MultipartFile file)
对文件的操作,对接收到的文件重命名,但保留原始的格式。
3)把url的前缀 跟我们设置的图片资源文件夹,即 D:/workspace/img
对应起来。
在 config\MyWebConfigurer
中添加代码
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/api/file/**").addResourceLocations("file:" + "d:/workspace/img/");
}
2、部署项目到服务器
选择一: 把前端项目部署在 web 服务器中,把后端项目部署在应用服务器中
选择二: 把前端项目打包,作为后端项目的静态文件,再把后端项目部署在应用服务器中
前后端分离项目,采用选择一
使用 web 服务器的好处有如下几点:可以实现反向代理,提高网站的安全性;方便维护,一些小的修改不必同时协调前后端开发人员;对静态资源的加载速度更快。
1、下载 nginx,解压缩
2、打开前端项目,执行 cnpm run build, dist 文件夹下将出现 static 和 index.html, 把它们拷贝进 nginx\html
下
3、nginx\conf\nginx.conf
,找到 server 的配置处,把 listen 80 改为 listen 8081; 添加一条 location 配置 try_files $uri $uri/ /index.html;
4、在前端 router\index.js
里添加一条路由
这样 访问 http://localhost:8081/ 会跳转到 /index,
运行 nginx 根目录下的 nginx.exe,启动web服务器
部署tomcat服务器,springboot自带
首先打开后端项目的 pom.xml
,修改 <packaging>
标签里的 war 为 jar ;maven install,项目的 target 文件夹下就会出现我们的 jar 包
java -jar wj-1.0.0.jar 即可运行