学艺不精,总是掉坑!前后端分离历险记

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u012702547/article/details/100418453

Spring Boot + Vue 这一对技术栈目前看来可以说是非常的火热,关于 Spring Boot 松哥已经写过多篇教程,如:

前后端分离的文章也写过好几篇了,例如:

相信大家也从中学到了不少干货。

老实说,前后端分离其实并不难,前后端分离之后,Java 工程师只需要专心写接口就可以了,在我看来工作可比以前轻松多了。

如果让一个专业的前端工程师来写前端页面,其实也不难,Vue 算是三大前端框架中最容易上手的了。

那怎么样就有难度了呢?

让同一个人既写前端又写后端!

我知道很多小伙伴在这里总是想不通,很多人 clone 了松哥在 GitHub 上的开源项目下来之后,问的最多的问题就是前后端是怎么通信的?跨域是怎么解决的?刚好松哥最近在这里踩了个坑,就来和大家聊一聊这个问题。

不是跨域的跨域

如果你直接在项目中引入 Vue,像用 jQuery 那样用 Vue,那没什么问题,你应该也不会有跨域的疑问。但是如果你做的是单页面应用(SPA),那么必然会有这样的疑问,跨域问题怎么搞!

因为在单页面应用中,前端项目可以单独通过 node 启动,它单独占用一个端口,后端项目启动后也是另外一个端口,此时从前端发送请求到后端,由于两者处于不同的端口之上,因此必然存在一个跨域问题。

但是大家想想,这个跨域有可能只是在开发环境下存在,生产环境下有可能不存在。因为当项目开发完成之后,我们对前端项目进行打包,打包后部署在 Nginx 上或者直接拷贝到后端项目中运行都可以(一般使用前者):

  • 如果是前者,后端接口也通过 Nginx 进行映射,这个时候就不会存在跨域问题了

  • 如果是后者,那就更简单了,部署的时候前后端代码放在一起,更不会有跨域问题了

因此,解决这个所谓的 “跨域” 问题,我们不能按照传统的思路来(通过 JSONP 或者 CORS),因为在项目真正上线后,所谓的跨域问题可能就会消失。

那么这个问题怎么解决呢?我们可以在前端 nodejs 中配置请求转发。

配置请求转发其实不难,不过 vue-cli2 和 vue-cli3 的写法稍有不同,这也是我前一段时间踩坑的地方。

vue-cli2 方案

如果我们使用的 vue-cli2 来创建的 SPA 应用,创建成功之后,在项目的 config 目录下有一个 index.js 文件,在这个文件中,我们可以进行请求转发配置,如下图:

640?wx_fmt=jpeg

配置内容如下:

module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
      '/': {
        target: 'http://localhost:8082',
        changeOrigin: true,
        pathRewrite: {
          '^/': ''
        }
      },
      '/ws/*': {
        target: 'ws://127.0.0.1:8082',
        ws: true
      }
    },
    ...
  }

proxyTable 就是我们配置的转发路由表。这个里边我们一共配置了两个规则:

  • 第一个是拦截所有 HTTP 请求,将之转发到后端服务器上(前端默认端口是 8080),后端的端口是 8082。至于拦截规则 / ,大家可以自定义,根据实际情况来写,例如所有的 HTTP 请求都有一个统一的前缀 api,那么这里就可以写 /api

  • 第二个是拦截所有的 websocket 请求进行转发,我这里给所有的 websocket 请求取了一个统一的前缀 /ws

如果你有更多的拦截规则,继续在这里配置就可以了,这些配置只会在开发环境下生效,当项目编译打包时,这些配置是不会打包进去的,也就是说,项目发布的时候,这些配置是失效的,这个时候我们通过 Nginx 或者将前端代码拷贝到后端,就可以解决生产环境下的跨域问题了(相当于开发时候的跨域在生产环境下不存在)。

相对来说,vue-cli2 在这里的配置还比较容易。

vue-cli3 方案

vue-cli3 去年出来后,当时就尝了一把鲜,但是可能 vue-cli2 用久了,一时半会还不愿意接受 vue-cli3 ,于是尝鲜完了之后就放下了,没怎么用了。直到前两天,新项目尝试了一下 vue-cli3,结果在请求转发这块就掉坑里了。

一开始没多想,还是 vue-cli2 里边的老办法,只不过是在 vue-cli3 创建的项目的 vue.config.js 文件中进行配置,文件位置如下图:

640?wx_fmt=jpeg

注意,使用 vue-cli3 创建的 SPA 应用,没有 config 目录了,因此请求转发的配置我们要在 vue.config.js 这个配置文件中来配置。

一开始我直接把 vue-cli2 中的请求转发配置拷贝过来,这样发送 HTTP 请求倒是没问题,但是 websocket 请求一直有问题,后来经过仔细分析,发现这两者在请求转发配置上有一点点差异,我们来看看 vue-cli3 中的请求转发配置(这也是我这里 vue.config.js 文件的完整内容);

let proxyObj = {};
proxyObj['/ws'] = {
    ws: true,
    target: "ws://localhost:8081"
};
proxyObj['/'] = {
    ws: false,
    target: "http://localhost:8081",
    changeOrigin: true,
    pathRewrite: {
        '^/': ''
    }
};
module.exports = {
    devServer: {
        host: 'localhost',
        port: 8080,
        proxy: proxyObj
    }
}

首先我们创建一个 proxyObj 用来放各种代理对象,至于代理的内容这里的则和 vue-cli2 中的没有太多差异。要注意的是,HTTP 请求代理中,多了一个属性 ws: false,用过 vue-cli3 同学可能发现了,如果不加这个属性,浏览器控制台会一直报连不上 socket 的错,加上就没事了。

最后在 devServer 中指定项目的 host 和 port ,然后再配置一下 proxy 对象就可以啦。

这就是我们在 vue-cli3 中请求的配置。

不过这里的配置老实说没有什么难度,做过一次就会啦,要是没做过,头一次可能得折腾半天。

结语

很多小伙伴一直对于前后端分离开发,前后端请求是如何对接的一直有疑问,希望这篇文章能够给你一些启发。如果看懂了,可以点个在看或者转发支持下哦。

640?wx_fmt=gif

640?wx_fmt=gif

1、一键部署 Spring Boot 到远程 Docker 容器,就是这么秀!

2、完结撒花!129 集 21 个小时,松哥自制的 Spring Boot2 系列视频教程杀青啦!

3、40 篇原创干货,带你进入 Spring Boot 殿堂!

4、Spring Boot 中的同一个 Bug,竟然把我坑了两次!

5、Spring Boot 支持 Https 有那么难吗?

6、一个野生程序员的自我修养

7、干货 | 鸟瞰 MySQL,唬住面试官!

8、Spring Boot 修改静态资源一定要重启项目才会生效吗?未必!

9、Spring Boot 跨域,JSONP 太 low,试试 CORS 怎么样?

640?wx_fmt=jpeg

喜欢就点个"在看"呗^_^

展开阅读全文

学艺不精,求大虾帮忙修改。

08-27

以下是我写的会议记录管理系统,但出错很多,求大虾帮我修改,并如何才能按部门储存到不同的盘。非诚勿扰!!!rnrnrnrn#includern#includernrn//会议类rnclass Meetingrnrnpublic:rnchar number[10];rnchar department[20];rnchar name[20];rnchar meeting_name[30];rnchar place[30];rnchar date[20];rnchar compere[10];rnchar recoder[10];rnchar information[500];rn;rnrn//函数声明rnvoid add(Meeting&);rnvoid scan(Meeting&);rnvoid refer(Meeting&);rnvoid change(Meeting&);rnvoid scan_way(Meeting&);rnvoid Delete(Meeting&);rnvoid save(Meeting&)rnvoid loading();rnrnrnrnrnvoid main()rnint c;rnMeeting meeting[15];rnloading();rnwhile(cin>>c,c!=0)rnrn switch(c)rn case 1:add(&meeting);break;rn case 2:scan(&meeting);break;rn case 3:refer(&meeting);break;rn case 4:change(&meeting);break;rn case 5:scan_way(&meeting);break;rn case 6:Delete(&meeting);break;rn case 7:save(&meeting);breakrn case 0:break;rn default:cout<<"对不起,你的输入有误,请重新输入。"<>num;rnm[i].number=num;rncout<<"请输入第"<>nam;rnm[i].name=nam;rncout<<"请输入第"<>depar;rnm[i].department=depar;rnrncout<<"请输入会议名称"<>meeting_na;rnm[i].meeting_name=meeting_na;rncout<<"请输入会议地点"<>pla;rnm[i].place=pla;rncout<<"请输入会议日期"<>dat;rnm[i].date=dat;rncout<<"请输入会议主持人"<>compe;rnm[i].compere=compe;rncout<<"请输入会议记录人"<>recod;rnm[i].recorder=recod;rncout<<"请输入会议摘要"<>infor;rnm[i].information=infor;rn;rnrnrnrnrn//浏览会议记录函数rnvoid scan(Meeting &m[15])rnrn for(int i=1;i<11;i++)rn rn cout<>nu;rn for(int i=1;i<11;i++)rn n=i;rn if(m.number[i]==nu)break;rn ;rn cout<<"会议编号"<>f;rnswitch(f)rncase 'a':m[11].number=11;rn cout<<"请输入出席人员名字"<>nam;rn m[i].name=nam;rn cout<<"请输入出席人员所属部门"<>depar;rn m[i].department=depar;rn cout<<"请输入会议名称"<>meeting_na;rn m[i].meeting_name=meeting_na;rn cout<<"请输入会议地点"<>pla;rn m[i].place=pla;rn cout<<"请输入会议日期"<>dat;rn m[i].date=dat;rn cout<<"请输入会议主持人"<>compe;rn m[i].compere=compe;rn cout<<"请输入会议记录人"<>recod;rn m[i].recorder=recod;rn cout<<"请输入会议摘要"<>infor;rn m[i].information=infor;break;rnrncase 'd':cout<<"请输入删除人员的编号"<>n;rn for(int i=n;i<11;i++)rn m.number[++i]=m.number[i];rn m.name[i]=m.name[++i];rn m.department[i]=m.department[++i];rn m.meeting_name[i]=m.meeting_name[++i];rn m.place[i]=m.place[++i];rn m.date[i]=m.date[++i];rn m.compere[i]=m.compere[++i];rn m.recorder[i]=m.recorder[++i];rn m.information[i]=m.information[++i];rn ;break;rnrnrnrnrn//排序浏览函数rnvoid scan_way(Meeting &m[15])rnrncout<<"从小到大浏览"<0;i--)rnrncout< 论坛

我要疯了,只怪学艺不精!!!

12-08

下面是一段动网7.0的sub footer函数代码。执行后如下:rnPowered By :Dvbbs Version7.0.0 de 菁英资讯网美化rnCopyright ©2002 - 2005 Bajiao.Netrn执行时间:62.50000毫秒。查询数据库5次。rn当前模板样式:[经典风格] rnrn我找死了,也没找到如何去掉"[color=#FF0000]菁英资讯网美化[/color]"rn[color=#FF0000]rn页面执行后查看源码效果是:[/color]rnPowered By :Dvbbs Version7.0.0 de 菁英资讯网美化 Copyright 省略。。。rnrn[color=#FF0000]源文件的函数代码重点是“这里是该版权的”。。我加单引号屏蔽了,但是菁英资讯网美化 这几个子都照样显示,全文也搜索不到,也没有id调用,除非把Response.Write Tmp1 加引号注释掉,才全部不显示。。我就奇怪了。到底哪里写了这句菁英资讯网美化 全文搜索也搜不到。。。。。。[/color]rnrn[code=VBScript]Public Sub Footer()rn Dim Tmp1,CaCheInforn Tmp1 = Replace(Tmp1,"$color",mainsetting(1))rn Tmp1 = Replace(Tmp1,"$width",mainsetting(0))rn '这里是该版权的。rnTmp1 = Replace(Tmp1,"$powered","Powered By :Dvbbs Version" & Forum_Version & " de")rn Tmp1 = Replace(Tmp1,"$Footer_ads",Forum_ads(1))rn If Forum_ChanSetting(0)="1" And Forum_ChanSetting(1)="1" And Forum_ChanSetting(4)="1" And IsTopTable=1 Thenrn Tmp1 = Replace(Tmp1,"$ad"," " & adcode_2)rn Elsern Tmp1 = Replace(Tmp1,"$ad","")rn End If rn Tmp1 = Replace(Tmp1,"$copyright",Forum_Copyright)rn Tmp1 = Replace(Tmp1,"$StyleName",StyleName)rn rn rn rn Response.Write Tmp1[/code] 论坛

学艺不精,难受中,搞不懂为什么要封装iocp

10-03

[code=C/C++]rnrnrniocp中工作线程这一个概念, 我翻看了不少资料,还有一些开源的iocp类,rnrn工作线程函数代码,都是同一个线程函数!!!rnrnrn我很奇怪,为什么工作线程的代码是一样的?rnrn比如:我已经有个需求,服务器需要开辟3个工作线程,一个应付文字传输,一个应付udp语音,rnrnrn一个应付文件传输。 自然 3个线程的代码是不一样的。 rnrnrn而不少开源的iocp类,作者在封装的时候,他们的工作线程函数代码都是一致的。然后留几个虚函数或者回调函数,让调用者自己实现。rnrnrn我提供一个例子,这是王艳萍的书上的一个例子,叫CIOCPServer(网上也广泛流传着CIOCPServer)rnrnrnrn////////////////////////////////////////////////////////////////////////////////////rnrnrn代码的大概思路: 注意HandleIO 和那几个虚函数 ,HandleIO是由线程函数_WorkerThreadPorc调用!!!!rnrn而虚函数则是被HandleIO调用!!!rnrnrnclass CIOCPServer // 处理线程rnrnpublic:rn .....................rnprotected:rn........................................rn rnrn void HandleIO(DWORD dwKey, CIOCPBuffer *pBuffer, DWORD dwTrans, int nError); //回调rnrnrn // 事件通知函数rn // 建立了一个新的连接rn virtual void OnConnectionEstablished(CIOCPContext *pContext, CIOCPBuffer *pBuffer);rn // 一个连接关闭rn virtual void OnConnectionClosing(CIOCPContext *pContext, CIOCPBuffer *pBuffer);rn // 在一个连接上发生了错误rn virtual void OnConnectionError(CIOCPContext *pContext, CIOCPBuffer *pBuffer, int nError);rn // 一个连接上的读操作完成rn virtual void OnReadCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer);rn // 一个连接上的写操作完成rn virtual void OnWriteCompleted(CIOCPContext *pContext, CIOCPBuffer *pBuffer);rnrnrnrnrnprivate: // 线程函数rn static DWORD WINAPI _ListenThreadProc(LPVOID lpParam);rn static DWORD WINAPI _WorkerThreadProc(LPVOID lpParam);rn;rnrnrnrnrn提供一个测试代码:rnrnrnclass CMyServer : public CIOCPServerrnrnpublic:rnrn void OnConnectionEstablished(CIOCPContext *pContext, CIOCPBuffer *pBuffer)rn rn printf(" 接收到一个新的连接(%d): %s \n", rn GetCurrentConnection(), ::inet_ntoa(pContext->addrRemote.sin_addr));rnrn SendText(pContext, pBuffer->buff, pBuffer->nLen);rn rnrn .......................rn;rnrnrnrn这种封装,似乎和我的需求冲突啊,我需要的是 3个线程,3个线程函数,而不是一个线程函数!!!rnrn而且HandleIO他都已经写好了。 线程函数中调用HandleIO.rnrnrnrn有人或许会说,你再换一个开源的 iocp不久可以了嘛? 我却是找了不少,发现,封装者差不多都是如此封装的。rnrn那么反推,我的设计有问题了!!!!(服务器打算用iocp模型, 可以中转语音,文字, 文件,我最初是想用3个线程,3个线程函数分别实现)rnrn如何 采用这些开源iocp来,来解决我的设计呢?rnrn[/code] 论坛

没有更多推荐了,返回首页