资讯动态

Servlet+Vue+AJAX构建前后端分离外卖点餐系统,打通Web开发全链路

发布时间:2026/9/8 7:33:45 来源:尧图企业网站定制
简介这是基于饿了么平台场景的Java后端项目采用Vue.js构建前端界面、Servlet负责后端业务处理并通过AJAX实现前后端数据异步交互是一套典型的前后端分离Web应用。项目包含完整的用户登录验证、AjaxServlet、数据库操作工具类等核心代码适合正在学习Java Web或想要了解前后端分离架构的开发者用于练手与参考。资源打包为zip格式压缩包大小4.15MB共131个文件文件类型涵盖Java源码、前端页面HTML/CSS/SCSS/LESS、Java class文件、配置文件及项目相关图片基本覆盖从界面展示、样式设计到业务逻辑与数据访问的完整链路。已有1434人学习或下载该资源。通过这套实战项目可以直观理解Vue与Servlet的交互方式掌握JSON数据交换、RESTful接口设计以及前后端分离环境下的部署思路对建立完整Web开发认知很有帮助。 做这个项目的时候我正在带一个刚转行学Java的朋友走一遍完整的Web开发流程。他一开始想直接上Spring Boot加Vue3但连POST请求的参数到底是怎么从浏览器跑到后端的都说不清楚。我就干脆让他把后端退回到最原始的Servlet用饿了么这个外卖点餐场景把前后端分离的整个链路从头到尾打通一遍。没想到这个倒退反而让很多东西都讲明白了所以我把这套项目的技术要点和实操经验整理出来给正准备入门或者想补一补基础的同学做个参考。这个项目的定位很明确用Servlet写后端接口用Vue写前端页面用AJAX做数据通信业务上模拟饿了么的外卖点餐流程。它不是什么高深架构但覆盖了登录注册、餐厅列表、菜品点选、购物车、订单提交这些常见业务模块刚好能把前端交互、HTTP协议、接口设计这些Web基本功全部串起来。1. 为什么选ServletVue这套组合做外卖项目1.1 从项目定位说起学原理而不是学框架现在很多人学Java后端spring boot一上来几个注解一加接口就莫名其妙能访问了。但稍微出点问题就抓瞎比如报了404不知道去哪看路由传了中文乱码不知道在哪设置编码。这些问题说到底都是对底层处理流程没有感知。Servlet是Java Web最原生的处理方式一个请求进来容器创建ServletRequest和ServletResponse调用service方法再分发到doGet或doPost。你亲手把这条路走一遍后面再上任何框架都会通透得多。这个项目把业务场景定为外卖点餐也是经过考虑的。外卖系统虽然简单但涉及的交互形式足够丰富有表单提交、列表查询、动态渲染、会话保持。用饿了么的表层业务来承载这套技术栈整体不会太复杂又能很好地覆盖前后端分离的各种典型场景。1.2 技术组合背后的几个关键考量前后端分离之所以选Vue做前端是因为它组件化的方式非常适合做餐厅列表、菜品卡片、购物车这种模块复用。Vue的响应式数据绑定让页面能实时响应后端返回的数据变化和AJAX异步请求配合起来非常顺手。后端的Servlet虽然老但它能让我们清楚看到接口从接收请求到返回响应的每一步这对理解整个数据流太有帮助了。我特意没有引入Spring Boot、MyBatis这些框架因为框架会屏蔽太多细节这个阶段看得见摸得着才最重要。AJAX这里我前端用的是axios但它的本质还是XMLHttpRequest只是做了一层Promise封装。用axios的好处是拦截器、统一错误处理这些写起来方便但它底下依然是浏览器的同源策略在管理跨域依然是异步回调的模式。把这些搞明白了面试时被问到底层原理也不虚。提示这个项目里我用的是Vue CLI创建的标准Vue 2项目没有用Vite主要是考虑到很多入门教程和插件生态还是基于Webpack的Vue CLI更稳。如果你熟悉Vite换成Vue 3完全没问题核心的交互逻辑是一样的。2. 后端Servlet接口设计从web.xml到JSON返回2.1 使用web.xml方式编写Servlet的完整示例虽然Servlet 3.0以后可以通过WebServlet注解直接配置但我这个项目特意保留了web.xml的方式。原因很实际很多老项目、学校的课程设计、以及一些企业的存量系统还在用web.xml而且web.xml把所有Servlet的映射关系集中在一处阅读和排查起来更直观。先看完整的web.xml配置?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 display-nameeleme-backend/display-name welcome-file-list welcome-fileindex.html/welcome-file /welcome-file-list servlet servlet-nameUserServlet/servlet-name servlet-classcom.eleme.servlet.UserServlet/servlet-class /servlet servlet-mapping servlet-nameUserServlet/servlet-name url-pattern/api/user/*/url-pattern /servlet-mapping servlet servlet-nameRestaurantServlet/servlet-name servlet-classcom.eleme.servlet.RestaurantServlet/servlet-class /servlet servlet-mapping servlet-nameRestaurantServlet/servlet-name url-pattern/api/restaurant/*/url-pattern /servlet-mapping servlet servlet-nameOrderServlet/servlet-name servlet-classcom.eleme.servlet.OrderServlet/servlet-class /servlet servlet-mapping servlet-nameOrderServlet/servlet-name url-pattern/api/order/*/url-pattern /servlet-mapping filter filter-nameCorsFilter/filter-name filter-classcom.eleme.filter.CorsFilter/filter-class /filter filter-mapping filter-nameCorsFilter/filter-name url-pattern/api/*/url-pattern /filter-mapping /web-appurl-pattern写成/api/user/*这种形式是故意把所有接口都收敛到/api前缀下面。这样不管前端还是后端一看路径就知道走的是接口层后面如果要加统一的过滤器或者权限校验直接对/api/*做处理就行。每个Servlet接收到请求后通过request.getPathInfo()获取后面的子路径用来区分是登录还是注册是查列表还是查详情。2.2 请求参数的读取与编码处理说到Servlet处理参数第一个拦路虎就是中文乱码。前后端分离之后请求可能来自各种环境编码问题比传统的JSP时代更明显。我在所有Servlet的doGet和doPost方法第一行都会写死两句话这已经是肌肉记忆了request.setCharacterEncoding(UTF-8); response.setContentType(application/json;charsetUTF-8);第一行负责把请求体的编码设为UTF-8。注意它必须放在读取任何参数之前才有效如果你先调用了getParameter再设置那这次请求已经按照默认ISO-8859-1解析完了再设置也白搭。第二行是告诉浏览器返回的数据是JSON格式、UTF-8编码这样前端才能正确解析中文。这里有个很容易被忽略的细节如果前端用axios发送的是application/json格式的数据request.getParameter()是拿不到参数的。因为Tomcat默认只解析application/x-www-form-urlencoded格式的请求体。所以我在前后端约定POST请求统一用application/x-www-form-urlencoded格式提交也就是用URLSearchParams来包装参数。如果你确实要传JSON格式后端就要手动读取request.getInputStream()来解析了这多出来的复杂度对这个项目来说没必要。2.3 统一的JSON响应结构与业务逻辑拆分为了让前端能统一处理结果我设计了一个简单的响应结构。所有接口返回的JSON都长这样{ code: 200, message: success, data: { ... } }对应的Java类是一个泛型类ResultDTO。code是业务状态码200表示成功其他数字表示各种业务异常。这样前端拿到响应后先判断code再决定是渲染数据还是弹出错误提示。不用走HTTP状态码那一套业务上的密码错误和网络上的404就能区分开了。Servlet内部的业务逻辑我按模块拆分到了Service层。UserServlet里只做参数接收、调用service、返回结果这三件事具体的校验和数据库操作放到UserService里。这样做的好处是这个项目以后如果要把Servlet升级成Spring BootServlet这层几乎可以全部扔掉Service层代码能直接复用。3. Vue前端环境搭建与核心页面实现3.1 从零搭建Vue开发环境并解决启动问题前端这一侧第一步是环境安装。很多新手在这里就卡住了我列一下完整流程先装Node.js建议装LTS版本我用的v16后面装依赖稳定很多。装完Node后npm就一起带上了然后全局安装Vue CLInode -v npm -v npm install -g vue/cli vue --version接着创建项目并启动vue create eleme-frontend cd eleme-frontend npm install npm run servevue create过程中会让你选预设我选的是Default (Vue 2)方便省事。项目跑起来之后控制台会输出两个访问地址一般是Local: http://localhost:8080/和Network: http://192.168.x.x:8080/。注意如果你遇到vue项目启动后Network不可用通常就是host配置的问题。macOS或Windows防火墙会拦截来自外部网络的访问。解决方法有两种一是在vue.config.js里把devServer的host设为0.0.0.0表示监听所有网络接口二是在操作系统防火墙里放行8080端口。开发的时候手机调试需要用到Network地址这个还是有必要处理的。3.2 路由结构与页面骨架设计前端页面我分成这几个登录页、首页餐厅列表、餐厅详情页、购物车页、订单列表页。路由直接用vue-router配置import Vue from vue import Router from vue-router import Login from ../views/Login.vue import Home from ../views/Home.vue import RestaurantDetail from ../views/RestaurantDetail.vue import Cart from ../views/Cart.vue import Orders from ../views/Orders.vue Vue.use(Router) const router new Router({ mode: history, routes: [ { path: /, redirect: /home }, { path: /login, component: Login }, { path: /home, component: Home }, { path: /restaurant/:id, component: RestaurantDetail }, { path: /cart, component: Cart }, { path: /orders, component: Orders } ] }) router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path ! /login !token) { next(/login) } else { next() } }) export default router路由守卫这里算是这个项目一个小亮点。beforeEach检查本地有没有token没有就强制跳到登录页。前后端分离的项目里前端路由守卫是保护页面安全的第一道关口后端接口还需要再做一层校验两层配合才是完整的方案。餐厅详情的/restaurant/:id这个参数前端通过this.$route.params.id拿这就是vue-router最常见的路由参数用法。3.3 axios请求封装与开发环境的代理转发我建议所有请求都走封装好的axios实例而不是在每个组件里直接axios.get。这样拦截器、统一错误处理、baseURL配置都能集中管理。封装的代码很简洁import axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { alert(res.message) return Promise.reject(new Error(res.message)) } return res }, error { alert(网络异常请稍后重试) return Promise.reject(error) } ) export default service请求封装好之后大问题就来了前端跑在8080端口后端跑在8081端口直接请求必然是跨域。这里我推荐开发环境用Vue CLI的代理功能它既简单又能完全绕开跨域问题因为代理转发是服务器到服务器不经过浏览器的同源策略。在vue.config.js里做如下配置module.exports { devServer: { port: 8080, host: 0.0.0.0, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } }前端页面请求/api/user/login时开发服务器会把它转发到http://localhost:8081/api/user/login。浏览器看到的所有请求都发往了8080同源没有跨域问题。代理转发配置重点是target要指向后端服务地址changeOrigin必须为true否则后端某些场景下拿到的Host头不对可能会有问题。4. 前后端联调实战登录与点餐流程的完整链路4.1 登录注册模块的前后端串联拿登录功能来说它的完整链路最能说明前后端分离的数据流。前端Login.vue里用户填写用户名和密码点击登录按钮后页面通过axios把参数发出去login() { const params new URLSearchParams() params.append(username, this.username) params.append(password, this.password) service.post(/user/login, params).then(res { if (res.data.token) { localStorage.setItem(token, res.data.token) this.$router.push(/home) } }) }后端的UserServlet的doPost方法里通过getParameter拿到username和password校验通过后生成一个token字符串放入ResultDTO返回。前端拿到token后存到localStorage然后通过this.$router.push跳转到首页。以后每个需要登录的接口请求都会在axios拦截器里自动带上这个token后端就能识别当前用户了。关于前后端分离项目的会话保持这里说一下我的处理思路。传统的不分离项目靠CookieSession但前后端分离后前端可能不在同一个域名下且移动端场景居多所以用Token更合适。当然这个项目为了简化token就是UUID生成的随机串存在服务端内存Map里。一旦服务重启所有用户都要重新登录这在生产环境不可取但对于教学项目完全够用了。等以后上了Redis再替换就好。4.2 餐厅列表的动态渲染与搜索首页加载时Home.vue在created钩子里请求餐厅列表接口created() { this.fetchRestaurants() }, methods: { fetchRestaurants() { service.get(/restaurant/list).then(res { this.restaurantList res.data.list this.loading false }) } }后端RestaurantServlet返回一个餐厅对象列表前端通过v-for指令渲染到卡片组件上。这一步看起来简单但它本质上就是AJAX异步加载数据的经典模式触发时机在生命周期钩子里请求发出后页面不阻塞数据返回后响应式更新视图。搜索功能则把输入框绑定到keyword变量监听input事件后重新调用查询接口把keyword作为请求参数传给后端做模糊匹配。4.3 购物车和订单从页面状态到服务端持久化购物车的实现是这个项目交互逻辑最重的一块。我的方案是购物车数据放在前端Vuex里管理用户点选菜品后同步更新Vuex中的state同时同步到localStorage做本地持久化。这样做的好处是用户操作零延迟不需要每个动作都请求后端也防止了页面刷新后购物车清空的尴尬。真正请求后端发生在提交订单的那一刻。前端把购物车里的菜品列表、餐厅id、总价打包成参数POST到/api/order/create。后端收到后生成一条订单记录并返回订单编号。前端拿到订单编号后跳转到订单详情页同时清空购物车。在订单列表页通过/api/order/list接口拉取当前用户的所有订单展示。这一步把前端状态管理和后端持久化做了清晰切割购物车是临时状态订单是持久化数据。实际开发中订单的金额计算要以后端为准前端传的价格只能做展示参考。我在OrderServlet里会重新查询菜品单价并计算总价防止用户篡改前端参数。这种安全细节虽然入门阶段用不上但养成习惯很重要。5. 开发中踩过的坑与排查经验5.1 跨域配置的坑预检请求与凭证我最初为了图省事没有配置前端代理而是直接在CorsFilter里放开跨域限制。看起来CorsFilter不复杂但实际联调的时候踩了两个坑。第一个是axios默认不会在请求里携带Cookie而Session机制依赖Cookie导致登录后接口仍然判定未登录。解决方法是前端在axios请求里设置withCredentials: true同时后端CorsFilter里不能再用Access-Control-Allow-Origin: *必须动态回显请求方的Origin头。第二个坑是浏览器发送POST请求前如果属于非简单请求会先发一个OPTIONS预检请求。Servlet容器默认不会把这个请求交给业务Servlet处理而是由CorsFilter直接拦截返回所以CorsFilter里需要对OPTIONS请求直接返回200不再向下传递。if (OPTIONS.equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return; }这一行代码不加前端控制台会出现一个Failed to load的报错而且网络面板里看不到实际业务请求排查半天都找不到原因。5.2 中文乱码的全链路排查思路前端的搜餐厅、下单、加购物车每一处涉及中文的地方都可能乱码。我自己遇到过前端传宫保鸡丁到后端变成???,从后端返回的餐厅名显示成乱码。这类问题的排查思路要按数据流一路看下来环节检查点解决方案前端页面HTML文件的meta声明UTF-8前端请求axios请求头Content-Type设置charsetUTF-8后端接收request.setCharacterEncoding必须在读取参数前调用后端返回response.setContentTypeapplication/json;charsetUTF-8数据库存储JDBC连接URL添加characterEncodingutf8Tomcat容器server.xml Connector配置URIEncodingUTF-8有一个很隐蔽的坑是如果前端是通过URL地址栏输入中文参数GET请求比如/api/restaurant/search?keyword烤鱼那Tomcat解析URL时用的编码是ISO-8859-1。这时候request.setCharacterEncoding不生效必须在server.xml的Connector节点加URIEncodingUTF-8才管用。所以我在这个项目里约定所有带中文的查询参数都用POST传从源头规避URL编码问题。5.3 AJAX参数格式不匹配的问题这是另一个典型的新手问题。axios默认提交对象时Content-Type会变成application/json如果后端还按表单方式getParameter取值就会得到null。我用的解决方案是统一用URLSearchParams包装参数。你也可以给axios设置全局转换service.defaults.headers.post[Content-Type] application/x-www-form-urlencoded但最稳妥的还是显式在提交时用URLSearchParams这样逻辑一目了然不会出现某个接口忘记设置的情况。如果后端确实需要接收JSON格式那就走request.getInputStream()手动解析这属于两种传参模式的应用场景之争关键是前后端要约定一致并且知道问题出在什么时候。5.4 关于打包部署的一些补充项目联调完成后前端通过npm run build会生成一个dist目录里面是静态文件。后端Servlet项目则打成war包部署到Tomcat。生产环境最简单的部署方式是让Nginx托管前端静态文件并把/api的请求反向代理到Tomcat。这样前端页面和接口在同一个域名下从根源上消除了跨域也避免了开发模式和生产模式的差异。很多人在这一步会把dist目录直接扔进Tomcat的webapps下面也能跑通但静态文件的性能远不如Nginx而且API接口和静态资源混在一起不好维护。我是倾向于单独部署的这个经验也分享给你。整个项目做下来我对前后端分离的理解比之前看十篇理论文章都深刻。尤其当你亲手把Servlet的请求接到Vue页面的那一刻整条链路就通了。这套技术栈虽然看起来不够新但作为练手项目它能把基础功打得很扎实。如果你现在正准备做类似的外卖点餐系统或者刚学完JavaWeb不知道下一步做什么我建议你也按这个思路走一遍。做完之后再去看看Spring Boot怎么做你会发现它的一切设计都变得理所当然。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价