工作不是很饱和的情况下,就有时间去研究一下技术上的东西,然后打算写一篇比较长的啰嗦的文章来记录一下
一、准备工作
首先要有一台云服务器(其他替代的能作为服务器的设备也行,最好是linux),然后我是提前配置好了java环境,OPENJDK或者ORACLEJDK都行,这两个的区别自行百度吧。
二、后端开发
新建一个springboot项目,然后可以用默认的起步依赖就行,我是多加了一个swagger的依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
目录结构如下所示
下面是SwaggerConfig类的代码,不过不用也没有啥影响
package com.zxc.springtest001.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration//托管spring
@EnableSwagger2//开启swagger功能
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket( DocumentationType.SWAGGER_2 )
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
.apiInfo( apiInfo() )
// 设置哪些接口暴露给Swagger展示
.select()
// (第一种方式)扫描所有有注解的api,用这种方式更灵活
.apis( RequestHandlerSelectors.withMethodAnnotation( ApiOperation.class ) )
// (第二种方式)扫描指定包中的swagger注解
//.apis(RequestHandlerSelectors.basePackage("com.zxc.springtest001.controller"))
// (第三种方式)扫描所有
//.apis(RequestHandlerSelectors.any())
.paths( PathSelectors.any() )
.build();
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo() {
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title( "标题:应用API文档" )
// 描述
.description( "描述:向前端提供应用的ResultFul风格接口文档" )
// 作者信息
.contact( "zhx" )
// 版本
.version( "版本号:" + "V1.0.0" )
.build();
}
}
InterfaceUtils和ResultVo就是封装了一下返回值的结构,没啥好说的
package com.zxc.springtest001.utils;
import com.zxc.springtest001.enums.ResultEnum;
import com.zxc.springtest001.vo.ResultVo;
public class InterfaceUtil {
public static ResultVo success() {
return new ResultVo(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getInfo(), null);
}
public static ResultVo success(Object data) {
return new ResultVo(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getInfo(), data);
}
public static ResultVo error(String info) {
return new ResultVo(ResultEnum.SERVICE_ERROR.getCode(), info, null);
}
public static ResultVo error(ResultEnum resultEnum) {
return new ResultVo(resultEnum.getCode(), resultEnum.getInfo(), null);
}
public static ResultVo error(ResultEnum resultEnum, String info) {
return new ResultVo(resultEnum.getCode(), info, null);
}
public static ResultVo error(Integer code, String info) {
return new ResultVo(code, info, null);
}
}
package com.zxc.springtest001.vo;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel
public class ResultVo<T> {
private Integer code;
private String info;
private T data;
}
主要逻辑就在Controller中,其实也是非常简单的两个方法,由于重点在部署,所以逻辑直接写在Controller中,但是实际开发中千万不要这样做
package com.zxc.springtest001.controller;
import com.zxc.springtest001.utils.InterfaceUtil;
import com.zxc.springtest001.vo.ResultVo;
import com.zxc.springtest001.vo.GetVo;
import com.zxc.springtest001.vo.TableList;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@CrossOrigin
@Api("测试方法controller")
public class TestController {
@ApiOperation(value = "get方法请求")
@GetMapping("/getList")
public ResultVo getList(String param){
if (param != null){
GetVo getVo = new GetVo();
getVo.setUserName("yiyuzz");
return InterfaceUtil.success(getVo);
}
return InterfaceUtil.error("传入参数为空");
}
@ApiOperation(value = "post方法请求")
@PostMapping("/postList")
public ResultVo<List<TableList>> postList(){
List<TableList> tableList = new ArrayList<>();
tableList.add(new TableList("张三","男","呜呜呜呜呜"));
tableList.add(new TableList("李四","女","啊啊啊啊啊"));
tableList.add(new TableList("王五","男","嗷嗷嗷嗷嗷"));
return InterfaceUtil.success(tableList);
}
}
然后配置文件里面写一下端口就行了,后端就写完了
三、前端开发
前端对我这种菜鸟来说还是太生疏了点,o(╥﹏╥)o。
框架是用的Vue,所以要先搭好环境,然后编译器的话IDEA、WebStorm、VScode都行,我是直接用vue脚手架创建了一个项目,然后执行npm install命令加载依赖,npm run dev 启动项目
要做修改的地方主要有两个
一是页面上要有一个向后端发送get请求的操作,还有一个发送post请求的操作
二是要通过axios发送请求包,接收响应包,并且携带参数,这里也涉及到跨域请求的问题,后面细说
先附上一个目录结构吧,要修改或者新增的红色标注
main.js文件如下所示,elementui和axios先引入
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.config.productionTip = false
Vue.use(ElementUI);
Vue.use(VueAxios,axios);
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
新建一个组件,然后把根路由指过去
import Vue from 'vue'
import Router from 'vue-router'
import FirstPage from '@/components/FirstPage'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'FirstPage',
component: FirstPage
}
]
})
然后新建一个http.js文件设置axios的请求拦截和响应拦截,其实这个拦截器只是做了简单的错误处理,然后设置了一下默认的baseURL(这个是访问后端地址的基础),还有要注意的是import是从axios依赖中导入的,然后拦截器写完了之后用export导出,这是为了后面封装请求方法时能直接使用拦截器。
import axios from 'axios';
axios.defaults.timeout = 20000;
// 返回其他状态吗
axios.defaults.validateStatus = function (status) {
return status >= 200 && status <= 500; // 默认的
};
// 跨域请求,允许保存cookie
axios.defaults.withCredentials = true;
//设置默认url
axios.defaults.baseURL = '/api';
axios.interceptors.request.use(
(config) => {
return config;
},
(error) => {
return Promise.reject(error);
}
);
// HTTPresponse拦截
axios.interceptors.response.use(
(res) => {
const status = Number(res.status) || 200;
// 如果是白名单类型放入catch自行处理
if (status !== 200) {
return Promise.reject(res);
}
return res.data;
},
(error) => {
return Promise.reject(new Error(error));
}
);
export default axios;
新建一个api.js封装请求方法,可以看到这里的axios是从http.js当中导入的,所以是直接带有拦截器的,然后封装了get和post方法(其实post方法里面的参数我没有用到,写不写都行),然后这里的url是一个相对url,完整的url是baseURL + url 这样,并且导出,让他们在项目其他组件可用
import axios from '../axios/http';
export const getList = (param) => {
return axios({
url: `/getList`,
method: 'get',
params: {
param,
}
});
};
export const postList = (params) => {
return axios({
url: `/postList`,
method: 'post',
data:params
});
};
const api = {
getList,
postList,
}
export default api;
然后就是FIrstPage这个页面了,是真的丑,这里getList方法是传入了参数,然后当执行created函数的时候调用,而点击按钮的时候执行post请求
<template>
<div align="left">
<h1>程序首页</h1>
<div >
我是:{{userName}}<br>
</div>
<div align="center">
<h3>正常的表单</h3>
<br>
<el-table class="table" :data="tableList" border >
<el-table-column prop="name" label="姓名" width="100"></el-table-column>
<el-table-column prop="sex" label="性别" width="100"></el-table-column>
<el-table-column prop="information" label="简介" width="100"></el-table-column>
</el-table>
<br>
<el-button @click="postList">获取表单数据</el-button>
</div>
</div>
</template>
<script>
import api from '../axios/api';
export default {
name: 'first-page',
data(){
return {
userName: '',
flag: '1',
tableList: [],
}
},
created(){
this.getList(this.flag);
},
mounted(){
// this.getList('1');
},
methods: {
getList(param){
api.getList(param).then((res) => {
// if (res.code === 0) {
console.log('get请求返回成功')
this.userName = res.data.userName;
// }
});
},
postList(params){
api.postList(params).then((res) => {
console.log('post请求返回成功')
this.tableList = res.data;
});
}
}
}
</script>
<style scoped>
.table {
text-align: center;
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 301px;
}
</style>
比较关键的就是配置一个本地代理来解决跨域的问题,这个在服务器上可以由nginx来处理,因为我的脚手架版本是2.9.6,所以找到config文件夹下index.js文件,修改proxyTable中的值,之前bsseURL中的'/api'就是用的这里配置的,而这个字符串的意义就是允许跨域的方式(changeOrigin)请求http://localhost:8081(后端地址和端口),前端地址可以看到是http://localhost:8082
到此为止,已经可以在本地启动一下前后端,联调测试一下功能
四、安装Nginx
先到Nginx官网下载一个安装包,既然是linux系统,那就下个tar.gz
服务器我是买的腾讯云的,然后连接工具用的putty,文件传输工具用winscp
然后安装前的环境准备,借鉴于大神的文章https://www.cnblogs.com/lywJ/p/10710361.html
1.需要安装gcc环境
# yum install gcc-c++
2.第三方的开发包
1 PERE
PCRE(Perl Compatible Regular Expressions)是一个Perl库,包括 perl 兼容的正则表达式库。
nginx的http模块使用pcre来解析正则表达式,所以需要在linux上安装pcre库。
注:pcre-devel是使用pcre开发的一个二次开发库。nginx也需要此库。
# yum install -y pcre pcre-devel
2 zlib
zlib库提供了很多种压缩和解压缩的方式,nginx使用zlib对http包的内容进行gzip,所以需要在linux上安装zlib库。
# yum install -y zlib zlib-devel
3 openssl
OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,
并提供丰富的应用程序供测试或其它目的使用。
nginx不仅支持http协议,还支持https(即在ssl协议上传输http),所以需要在linux安装openssl库。
# yum -y install pcre pcre-devel zlib zlib-devel openssl openssl-devel
然后通过WinSCP工具把安装包上传到服务器,随便找个喜欢的文件夹放吧
用下面的命令将压缩包解压到/usr/local目录下
# tar -xvf nginx-1.14.0.tar.gz -C /usr/local
使用configure命令创建一个makeFile文件
./configure \
--prefix=/usr/local/nginx \
--pid-path=/usr/local/nginx/logs/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi \--with-http_stub_status_module \--with-http_ssl_module \--with-file-aio \--with-http_realip_module
请进入nginx-1.18.0目录下执行configure命令,然后和原文章相比,我修改了pid-path的路径,因为原文中这个路径会导致nginx重启报错
注意:启动nginx之前,上边将临时文件目录指定为/var/temp/nginx,
需要在/var下创建temp及nginx目
创建目录/var/temp/nginx/
# mkdir /var/temp/nginx -p
-p 表示级联创建的意思
进入nginx-1.18.0里面执行make命令进行编译
[root@VM-0-13-centos nginx-1.18.0]# make
进入nginx-1.18.0里面执行make install 命令进行安装
这里不需要再次执行安装路径,创建makefile文件的时候已经指定了。
[root@VM-0-13-centos nginx-1.18.0]# make install
进入安装位置/usr/local/nginx查看目录结构
其中html是里面首页html文件。conf里面是配置文件。sbin里面只执行文件。我新建了logs文件用来存放日志,也是为了解决pid文件的问题
进入sbin目录,执行命令./nginx
[root@VM-0-13-centos sbin]# ./nginx
查看nginx是否启动
[root@VM-0-13-centos sbin]# ps -aux | grep nginx
ps命令用于报告当前系统的进程状态。
-a:显示所有终端机下执行的程序,除了阶段作业领导者之外。
a:显示现行终端机下的所有程序,包括其他用户的程序。
u:以用户为主的格式来显示程序状况。
x:显示所有程序,不以终端机来区分。
关闭nginx
[root@admin sbin]# ./nginx -s stop
或者
[root@admin sbin]# ./nginx -s quit
刷新配置文件
[root@admin sbin]# ./nginx -s reload
关闭防火墙,开启远程访问
首先需要关闭防火墙:默认端口是80
方法一:永久开放80端口
/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT /etc/rc.d/init.d/iptables save
方法二:临时关闭系统防火墙
# service iptables stop
方法三:永久关闭修改配置开机不启动防火墙
# chkconfig iptables off
如果成功启动了nginx,那可以在浏览器直接访问服务器地址测试一下,成功的话会有welcome to nginx页面
五、部署应用
首先是前端使用npm run build 打包,生成dist文件夹
后端使用maven install命令打包就可以,会在target目录下面生成一个可执行jar
把后台打好的包放到随便一个自己能找到的文件夹下面,要保证有java运行环境就行
把前端的dist文件夹下面的内容放到nginx的html目录下面,覆盖原有的index.html文件
然后就是要配置一下nginx的conf配置文件,这个是nginx起作用的关键,无论是当一个http服务器用,还是做负载均衡,或者是虚拟主机,配置文件都会起到非常关键的作用
这个文件有一些默认的配置,针对于当前项目,我们只需要修改server中的内容就可以了,listen监听80端口(前提是保证服务器的防火墙不会拦截80端口),server_name由于我没有申请域名,也不想改hosts文件,就直接用localhost服务器地址了
我们已经把前端vue项目部署到nginx的html文件夹下面了,所以root根路径就设置到html文件夹,首页就是vue的index.html
后台的访问需要配置代理,和我们在本地配置的思路一样,需要注意的是proxy_pass后面的地址是后端程序的访问地址和端口,记得地址最后一定要加 / 保证代理会起作用
配置好之后 我们重启nginx然后加载一下新的配置文件,然后检查一下nginx的进程
使用nohup java -jar xxx &命令启动后台程序
OK,现在来访问一下!
首页进入的时候通过getList方法获取到用户,然后点击按钮获取表单数据