前言
继承上一篇博文,本篇主力演示利用 jQuery ajax 实现 SPA 的前后端分离。本例子后端为一个简单的 spring boot 项目。
前端 Ajax
创建 login.js 和 修改 Bootstrap Form 例子
为方便起见,不用每次重新加载页面后跳转到首页,我们修改 index.js 里的 defaultPage 和 defaultNavId 为 Login 及 navLoginLink(Login 页面和菜单选项)
改造 Bootstrap 的表单示例,加入 form id,method 和 action。同时,提交按钮也加入 button id=“submitButton”,如下:
修改完成后,点击表单提交按钮后,路径变化符合预期成为后端路径。(页面报错符合预期,因为还没搭建后端服务器)
架设 Nginx 反向代理解决跨域问题
然而后端服务器不会在5500端口提供服务。更改端口成为后端端口的话,将会衍生跨域访问问题。为了避免跨域问题,我们需要设置一个 http server 提供反向代理。本文以 Nginx 为例,简单的设置一下反向代理服务。(详细的设置 Nginx 反向代理服务,请参考本栏另一博文 Vue + Nginx 项目部署 (亲测有效))设置如下:
- 首先在 nginx.conf 加入新的 server 配置(假设前端在5500端口、后端在15500端口)
在命令行输入
nginx -t
测试一下新写的配置有没有问题,没问题的话,输入
start nginx 或
nginx -s reload
启动/重启 Nginx 服务器。现在前端和后端处于相同域名和端口了。跨域问题解决。
前端加入 ajax 代码提交表单到后端
在所有前后端分离项目,首先我们需要关闭提交按钮的默认操作,并把提交表单数据的任务交给 ajax 去处理。在 index.js 里加入以下代码:
现在点击登录按钮,我们发现表单没有被提交到后端了。
现在,那么我们继续加入一些表单处理代码 和 ajax 代码:
在 e.stopPropagation() 下面,我们首先把所有表单里所有的输入项加入到一个表单类(ssoLoginFormOject)里。
看到 ssoLoginFormOject 最后多出了 ,“”:“”。感觉$(‘#ssoLoginForm :input’) 把 button 也拉进来了。这个 button 我们不需要提交。,在 index.js 里 $(‘#ssoLoginForm :input’) 加个条件,遇到 id===”submitButton" 就不处理
再提交一次表单,发现 SSOLoginFormObject 没有了 “”:“”。OK了。
然后通过 jQuery ajx 把表单内容以 json 格式发送到后台并作进一步处理。如下:
再次提交表单,看看效果
提交表单后,ajax 返回 502 Bad Gateway 错误信息。原因很简单,因为我们还没有部署后端服务器。
后端 Spring Boot(Rest Controller)
新建一个 Spring Boot Maven 项目,
引入 Spring Web, Lombok 依赖
完成后 pom.xml 长这样
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache/POM/4.0.0" xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache/POM/4.0.0 https://maven.apache/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hivesplace.blog</groupId>
<artifactId>hp-blog-oauth2-auth-server-15505</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hp-blog-oauth2-auth-server-15505</name>
<description>Hiveplace Blog - oAuth2 Authorization Server</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<repositories>
<repository>
<id>projectlombok</id>
<url>http://projectlombok/mavenrepo</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
项目架构
在 src/main/java com.hivesplace.blog 右键加入 controller package
在 controllers package 内新增 LoginController 类
LoginContoller.java
package com.hivesplace.blog.controllers;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
@PostMapping("/login")
public ResponseEntity<ResponseDetails> login(@RequestBody SsoLoginFormDTO ssoLoginFormDTO) {
return new ResponseEntity<ResponseDetails>(new ResponseDetails(200, "OK", ssoLoginFormVO), HttpStatus.OK);
}
}
我们用统一返回方式,设置统一回复类型 ResponseEntity<ResponseDetails>。前端传值和后端回复统一同 json 格式。
基于这个做法,LoginController 内需要多加入三个 package 和三个类,分别是 beans,DTOs 和 VOs package;ResponseDetails,SsoLoginFormDTO 和 SsoLoginFormVO 类。
- ResponseDetails.java
package com.hivesplace.blog.beans;
import lombok.Data;
@Data
public class ResponseDetails {
public ResponseDetails() {}
public ResponseDetails(int code, String description, Object objectToFE) {
this.setCode(code);
this.setDescription(description);
this.setObjectToFE(objectToFE);
}
private int code;
private String description;
private Object objectToFE = new Object();
}
- SsoLoginFormDTO.java
package com.hivesplace.blog.DTOs;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@Component
public class SsoLoginFormDTO {
private String inputEmail;
private String inputPassword;
private String inputRememberMe;
}
- SsoLoginFormVO.java
package com.hivesplace.blog.VOs;
import org.springframework.stereotype.Component;
import lombok.Data;
@Data
@Component
public class SsoLoginFormVO {
private String status;
}
定义好这三个类后,我们再修改一下 LoginController.java
@RestController
public class LoginController {
@Autowired
SsoLoginFormVO ssoLoginFormVO;
@PostMapping("/login")
public ResponseEntity<ResponseDetails> login(@RequestBody SsoLoginFormDTO ssoLoginFormDTO) {
ssoLoginFormVO.setStatus("success");
return new ResponseEntity<ResponseDetails>(new ResponseDetails(200, "OK", ssoLoginFormVO), HttpStatus.OK);
}
}
最后在 /src/main/resources 里加入 application.yml,配置后端项目路径 /oauth2
application.yml
server:
port: 15500
servlet:
context-path: /oauth2
spring:
application:
name: hp-blog-oauth2-auth-server-15500
启动后端 Spring Boot 服务器
把后端返回的结果返回并显示在 SPA 页面上
我们在前端 Login 页面重新提交登录表单,这次发现后端返回成功登录的信息了。
按惯常做法,成功登录后会跳转到用户页面。失败的话,在原登录页面会提示登录失败字样。我们现在处理一下
新增用户 Member 页面
修改 index.html,在 <div id=“Login”></div> 下面增加 <div id=“Member”></div>
在 index.css 增加 Member 内容样式
在 index.js 新增以下四项(新增 Member 菜单、鼠标响应等):
成功登录的效果
然后我们再尝试提交表单并看看成功登录的效果
登录失败的处理及效果
现在我们处理一下登录失败的处理
功能要求
假设本 SPA 的有效用户和密码分别是 spa@hivesplace 及 spa123,如果登录的用户名称和密码组合不符合,那么后端就响应登录失败信息。
- 在 index.html Login DIV 内新增一个 div 块。
- 在 login.css 内增加 loginResultMessage 样式
- 修改 index.js 里 ajax error: function 代码,如下:
修改后端统一返回信息
- 在 LoginController 加入用户数据判断
重启后端服务器
完成效果
后续更新
后续我们会示范利用 Spring Data JPA + MySQL 实现从数据库查询用户名称和密码
源代码
关注我并发表不少于10字评论可以获取本文源代码。
码农经典语录
Linus Torvalds
Talk is cheap, show me the code.
蜂窝码农
- DRY Principle (Don’t Repeat Yourself) 是做技术的最大笑话, DRY Principle应该改成 DORY Principle (Do Repeat Yourself)才对
- 没有中国参与的标准,不能称为世界标准*。
俗语
好读书、好记性不如烂笔头
更多推荐
H5+javascript+jQuery的单页面应用(SPA)(三)
发布评论