前言

继承上一篇博文,本篇主力演示利用 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 项目部署 (亲测有效))设置如下:

  1. 首先在 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 类。

  1. 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();
}
  1. 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;

}
  1. 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,如果登录的用户名称和密码组合不符合,那么后端就响应登录失败信息。

  1. 在 index.html Login DIV 内新增一个 div 块。
  2. 在 login.css 内增加 loginResultMessage 样式
  3. 修改 index.js 里 ajax error: function 代码,如下:

修改后端统一返回信息

  1. 在 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)(三)