跳至主要內容

SpringBoot与WEB应用-1

wangdx大约 24 分钟

整合 WEB 监听器

WEB 监听器

  • 监听器是 Servlet 标准规范中定义的一种特殊类,用于实现 ServetContext、HttpSession 以及 ServetRequest 等域对象的创建和销毁事件,同时也可以监听域对象属性发生的增加、修改以及删除操作时的处理,在 SpringBoot 中可以直接整合监听器组件,并基于原始的 WEB 监听事件进行处理,
1、
package com.yootk.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class WebServletListener implements ServletContextListener { // 配置监听器
    @Override
    public void contextInitialized(ServletContextEvent sce) { // 上下文初始化
        System.out.println("【WebServletListener】Servlet初始化:" + sce.getServletContext().getServerInfo());
        System.out.println("【WebServletListener】Servlet初始化:" + sce.getServletContext().getRealPath("/"));
        System.out.println("【WebServletListener】Servlet初始化:" + sce.getServletContext().getVirtualServerName());
    }
}


2、
package com.yootk; // 父包,这个包中的所有子包的类会被自动扫描

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication // 一个注解解决所有的问题
// 如果此时你项目中的过滤器使用了配置类的方式配置,则配置监听器的时候为了防止重复Bean注册,所以指定扫描包名称
@ServletComponentScan({"com.yootk.listener"}) // Servlet组件扫描
public class StartSpringBootApplication extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(StartSpringBootApplication.class,args); // 运行SpringBoot程序
    }
}


拦截器

Spring 拦截器

  • 除了 WEB 组件提供的过滤器拦截之外,在 SpringMVC 又根据自身的业务需要提供了拦截器的概念,这样可以在控制器将请求分发到具体的 Action 之前或之后对请求或响应的操作实现拦截处理
1、
package com.yootk.interceptor;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DefaultHandlerInterceptor implements HandlerInterceptor { // 自定义拦截器

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) { // 判断是否为指定类型实例
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            System.err.println("【Action对象】" + handlerMethod.getBean()); // 控制器实例
            System.err.println("【Action类型】" + handlerMethod.getBeanType());
            System.err.println("【Action方法】" + handlerMethod.getMethod());
        }
        return true; // 放行
    }
}


2、
package com.yootk.config;

import com.yootk.interceptor.DefaultHandlerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration // 配置类
public class WebInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) { // 追加拦截器
        registry.addInterceptor(this.getDefaultHandlerInterceptor()).addPathPatterns("/**"); // 配置拦截路径
    }
    @Bean
    public HandlerInterceptor getDefaultHandlerInterceptor() {
        return new DefaultHandlerInterceptor();
    }
}

3、localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19

AOP 拦截器

AOP 拦截器

  • 在 Spring 开发框架中 AOP 主要是编写切面代码,实现业务调用的切面拦截控制,利用 Aspect 表达式在不使用任何的侵入式代码编写方式下,可以方便的实现代码切面代码的植入

引入 AOP 依赖

  • 如果要想在 SpringBoot 中实现 AOP 切面拦截,则首先需要修改项目中的“build.gradle”配置文件(microboot 父项目中的配置文件)引入 org.springframework.boot:spring-boot-starter-aop”依赖库配置
1、
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: '2.4.3'

2、
project('microboot-web') { // 子模块
    dependencies { // 配置子模块依赖
        compile(project(':microboot-common')) // 引入其他子模块
        compile('org.springframework.boot:spring-boot-starter-aop') // 引入AOP依赖库
    }
}

3、
package com.yootk.service;

public interface IMessageService { // 创建业务接口
    public String echo(String msg);
}


4、
package com.yootk.service.impl;

import com.yootk.service.IMessageService;
import org.springframework.stereotype.Service;

@Service
public class MessageServiceImpl implements IMessageService {
    @Override
    public String echo(String msg) {
        return "【ECHO】" + msg;
    }
}


5、
package com.yootk.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect // 实现切面处理类
public class ServiceAspect { // 实现AOP切面管理
    @Around("execution(* com.yootk..service..*.*(..))") // 定义好了当前的切面
    public Object arroundInvoke(ProceedingJoinPoint point) throws Throwable {
        System.out.println("【ServiceInvokeBefore】执行参数:" + Arrays.toString(point.getArgs()));
        Object obj = point.proceed(point.getArgs()); // 调用真实业务主题
        System.out.println("【ServiceInvokeAfter】返回结果:" + obj);
        return obj; // 需要返回执行的结果
    }
}

6、
package com.yootk.test;

import com.yootk.StartSpringBootApplication;
import com.yootk.service.IMessageService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;

@ExtendWith(SpringExtension.class) // 使用JUnit5测试工具
@WebAppConfiguration // 启动WEB运行环境
@SpringBootTest(classes = StartSpringBootApplication.class) // 配置程序启动类
public class TestMessageService { // 编写测试类
    @Autowired
    private IMessageService messageService;
    @Test
    public void testEcho() {    // 进行响应测试
        String content = this.messageService.echo("沐言科技:www.yootk.com");
        String value = "【ECHO】沐言科技:www.yootk.com";
        System.err.println("【@Test】测试echo()方法返回值,当前放回结果为:" + content);
        Assertions.assertEquals(content, value); // 测试相等
    }
}

整合 Email 邮件服务

邮件发送与接收

  • Email 是一种在互联网上出现较早并且已经被广泛使用的一种 WEB 服务,不同的编程语在 Java 中开发者可以通过 JavaMail 实现言针对于 Email 的使用也都提供有各自的包装邮件系统的开发,而在进行 JavaMail 开发之前,需要首先提供有一个“SMTP/POP3 的邮件服务器

  • SMTP(Simple Mail Transfer Protocol、即简单邮件传输协议):它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器。
  • POP3(Post Office Protocol3、邮局协议的第 3 个版本):规定怎样将个人计算机连接到 Internet 的邮件服务器和下载电子邮件的电子协议,主要是用于接收邮件内容的协议。
1、ccddjswrussfdafsdfff

2、
project('microboot-web') { // 子模块
    dependencies { // 配置子模块依赖
        compile(project(':microboot-common')) // 引入其他子模块
        compile('org.springframework.boot:spring-boot-starter-mail')
    }
}

3、
server:
  port: 80
spring:
  mail:
    host: smtp.qq.com # 一定要使用合法的SMTP
    username: 784420216@qq.com # 用户名
    password: ccddjswruoddbfff # 是临时生成的密码
    properties:
      mail.smtp.auth: true # 启用SMTP认证
      mail.smtp.starttls.enabled: true # 启用SMTP认证
      mail.smtp.starttls.required: true # 必须采用加密链接

4、
package com.yootk.test;

import com.yootk.StartSpringBootApplication;
import com.yootk.service.IMessageService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;

@ExtendWith(SpringExtension.class) // 使用JUnit5测试工具
@WebAppConfiguration // 启动WEB运行环境
@SpringBootTest(classes = StartSpringBootApplication.class) // 配置程序启动类
public class TestSendEmail { // 编写测试类
    @Autowired
    private JavaMailSender javaMailSender; // 发送邮件工具类
    @Test
    public void testSend() {    // 进行响应测试
        SimpleMailMessage message = new SimpleMailMessage(); // 建立一个简单的邮件结构
        message.setFrom("784420216@qq.com");
        message.setTo("2200934106@qq.com"); // 邮件的接收者
        message.setSubject("来自爆可爱的小李老师给你的祝福。");
        message.setText("沐言科技(www.yootk.com)");
        this.javaMailSender.send(message); // 邮件发送
    }
}

https 安全访问

HTTPS

  • HTTPS(Hyper Text Transfer Protocol over SecureSocket Layer)是以安全为目标的 HTTP 传输 通道,在已有的 HTTP 协议的基础上通过传输加密和身份认证保证了传输过程的安全性。。如果要想实现 HTTPS 的传输,那么首先要通过 CA 机构获取到一个官方认证的 HTTPS 证书,而如果现在开发者没有证书也可以直接通过 Java 提供的 keytool 命令生成一个本地的模拟证书
  • keytool -genkey -alias yootkServer -storetype PKCS12 -keyalg RSA -keysize 2048 -keystorekeystore.p12 -validity 3650 -dname"CN=YootkWebServer,OU=Yootk,0=MuyanYootk,L=Beiling,S=Beiing,C=China" -storepassyootkiava

HTTPS 证书配置

  • 格式的证书内容,同时定义了该证书本程字通过 keytodl 生成了-个“Keystore.D2”的别名为“yootkServer"而要想引用此证书"yootkjava”首先需要将此证书配置到项目的 CLASSPATH 路径之中,为便于管理,本次将证书拷贝到 SpringBoot 项目中的“src/main/resources”路径之中

如果使用了上面的配置就表示 springboot 应用程序不再在端口 8080 上支持 HTTP 连接请求,SpringBoot 不能通过配置 application.properties 来实现既支持 HTTP 连接又支持 HTTPS 连接,这是做不到的,如果要同时支持 HTTP 和 HTTPS,则需要以编程方式配置其中的一个,建议使用 application.properties 文件来配置 HTTPS,以编程方式配置 HTTP,这是比较容易的方法;

原文链接open in new window

Spring Boot 工程支持 HTTP 和 HTTPS,HTTP 重定向 HTTPSopen in new window

1、
keytool -genkey -alias yootkServer -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 -dname "CN=YootkWebServer,OU=Yootk,O=MuyanYootk,L=BeiJing,S=BeiJing,C=China" -storepass yootkjava

2、
server:
  port: 443 # https访问端口一定是443
  ssl: # 所有的证书都是在容器中配置
    key-store: classpath:keystore.p12 # 配置证书的路径
    key-alias: yootkServer # 证书别名
    key-store-type: PKCS12 # P12证书格式
    key-store-password: yootkjava

3、
https://localhost/message/message?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19


4、
http://localhost/message/message?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19

5、
package com.yootk.config;

import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HttpConnectorConfig { // 配置HTTP连接器
    public Connector getHTTPConnector() { // 获取新的连接器
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); // 配置连接器的处理协议
        connector.setScheme("http"); // HTTP协议访问
        connector.setSecure(false); // 非安全传输
        connector.setPort(80); // HTTP监听端口
        connector.setRedirectPort(443); // 强制跳转到443端口
        return connector;
    }
    @Bean // 自动配置
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
            @Override
            protected void postProcessContext(Context context) { // 发送处理
                SecurityConstraint securityConstraint = new SecurityConstraint();
                securityConstraint.setUserConstraint("CONFIDENTIAL"); // 设置约束
                SecurityCollection collection = new SecurityCollection();
                collection.addPattern("/*"); // 所有的路径全部进行处理
                securityConstraint.addCollection(collection);
                context.addConstraint(securityConstraint);
            }
        };
        tomcat.addAdditionalTomcatConnectors(this.getHTTPConnector());
        return tomcat;
    }
}

http://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19

全局错误页

错误页配置

  • SpringBoot 在设计之初考虑到了项目中的错误页的处理,一旦发生错误后,会自动的跳转到一个内置的错误页进行用户请求响应,但是该错误页所能够描述的信息有限,而如果要想在错误页中显示更多的信息,则就需要开发者根据自身项目的业务需要进行错误页内容的配置
1、
	http://localhost/muyan/yootk

2、
package com.yootk.action;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/errors/*") // 父路径
public class ErrorPageAction { // 用于进行错误页的处理
    @RequestMapping("error_404")
    public Object errorCode404() {  // 没有发现请求路径
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setStatus(HttpServletResponse.SC_NOT_FOUND); // 设置响应编码
        Map<String, Object> map = new HashMap<>(); // 即便是错误页实际上也是一个正常响应
        // map.put("status", HttpServletResponse.SC_NOT_FOUND); // 响应编码
        map.put("status", 404); // 响应编码
        map.put("content", "无法找到用户访问路径。"); // 适当性的带一点文字描述
        map.put("referer", request.getHeader("Referer")); // 获取之前的来源
        map.put("path", request.getRequestURI()); // 访问路径
        return map;
    }
}


3、
package com.yootk.config;

import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

@Configuration
public class ErrorPageConfig implements ErrorPageRegistrar { // 错误页注册
    @Override
    public void registerErrorPages(ErrorPageRegistry registry) { // 页面注册
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/errors/error_404"); // 定义404错误页
        registry.addErrorPages(errorPage404); // 已经添加了新的错误页
    }
}


4、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.vo.Message;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo") // 子路径
    public Object echo(Message message) { // 进行请求参数的接收以及请求内容的回应
        message.setTitle("【ECHO】" + message.getTitle());
        message.setContent("【ECHO】" + message.getContent());
        return message;
    }
    @RequestMapping("calc")
    public Object calc(int x, int y) {
        return x / y;
    }
}
// http://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19
// https://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19

5、
http://localhost/message/calc?x=10&y=1
http://localhost/message/calc?x=10&y=0

6、
package com.yootk.action;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/errors/*") // 父路径
public class ErrorPageAction { // 用于进行错误页的处理
    @RequestMapping("error_404")
    public Object errorCode404() {  // 没有发现请求路径
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setStatus(HttpServletResponse.SC_NOT_FOUND); // 设置响应编码
        Map<String, Object> map = new HashMap<>(); // 即便是错误页实际上也是一个正常响应
        // map.put("status", HttpServletResponse.SC_NOT_FOUND); // 响应编码
        map.put("status", 404); // 响应编码
        map.put("content", "无法找到用户访问路径。"); // 适当性的带一点文字描述
        map.put("referer", request.getHeader("Referer")); // 获取之前的来源
        map.put("path", request.getRequestURI()); // 访问路径
        return map;
    }
    @RequestMapping("error_500")
    public Object errorCode500() {  // 没有发现请求路径
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 设置响应编码
        Map<String, Object> map = new HashMap<>(); // 即便是错误页实际上也是一个正常响应
        map.put("status", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 响应编码
        map.put("content", "服务器端程序出错。"); // 适当性的带一点文字描述
        map.put("referer", request.getHeader("Referer")); // 获取之前的来源
        map.put("path", request.getRequestURI()); // 访问路径
        return map;
    }
}

7、
package com.yootk.config;

import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

@Configuration
public class ErrorPageConfig implements ErrorPageRegistrar { // 错误页注册
    @Override
    public void registerErrorPages(ErrorPageRegistry registry) { // 页面注册
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/errors/error_404"); // 定义404错误页
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/errors/error_500"); // 定义500错误页
        registry.addErrorPages(errorPage404, errorPage500); // 已经添加了新的错误页
    }
}

全局异常处理

全局异常处理

  • 在程序开发中,永远都无法保证所执行的代码不会产生异常信息,所以当出现异常之后希望可以对所有的异常进行统一的捕获与处理,同时也可以将程序中所产生的异常信息详细的发送给响应客户端

如果要想实现异常处理操作,那么首先需要定义一个异常处理类,同时该类中要使用"@ControlerAdvice",在该类中可以由用户任意定义一个异常处理方法,但是需要明确的通过“@ExceptionHandler”进行异常的处理声明。

1、
package com.yootk.advice;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionAdvice {
    // 如果说你现在是一个包含有完整业务需求的项目,可以在此处填写上一些自定义的业务异常;
    @ExceptionHandler(Exception.class) // 可以捕获的异常类型
    @ResponseBody // 本次的处理是基于Rest风格完成的
    public Object exceptionHandler(Exception e) {  // 实现所有的异常处理
        Map<String, Object> map = new HashMap<>(); // 保存响应信息
        map.put("message", e.getMessage()); // 直接获取异常信息
        map.put("status", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 设置一个HTTP状态码
        map.put("exception", e.getClass().getName()); // 获取异常类型
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        map.put("path", request.getRequestURI()); // 异常发生的路径
        return map; // 直接返回对象
    }
}


2、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/create/*") // 添加父路径
public class CreateExceptionAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("exception")
    public Object create() {
        return 10 / 0;
    }
}

全局数据绑定

全局数据绑定 在每一次用户发出请求时,有可能需要附带有一些公共的数据内容要随着请求一起发送到指定的 Action 类中进行处理,同时考虑到该内容的可维护性,可以基于“@ControlerAdvice”实现全局数据绑定处理类

1、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/data/*") // 添加父路径
public class GlobalDataAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo")
    public Object echo(String message) {
        Map<String, Object> map = new HashMap<>();
        map.put("title", "沐言科技");   // 所有的相应都需要提供有此类数据
        map.put("content", "www.yootk.com");   // 所有的相应都需要提供有此类数据
        map.put("data", "【ECHO】" + message); // 不同的处理有可能会返回不同的数据结果
        return map; // 通过Map返回数据

    }

    @RequestMapping("calc")
    public Object calc(int x, int y) {
        Map<String, Object> map = new HashMap<>();
        map.put("title", "沐言科技");   // 所有的相应都需要提供有此类数据
        map.put("content", "www.yootk.com");   // 所有的相应都需要提供有此类数据
        map.put("data", x / y); // 不同的处理有可能会返回不同的数据结果
        return map; // 通过Map返回数据
    }
}

2、

localhost/data/echo?message=李兴华高薪就业编程训练营(edu.yootk.com)
localhost/data/calc?x=10&y=5

3、
package com.yootk.config;

import com.yootk.vo.Message;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MessageConfig {
    @Bean
    public Message getMessage() {
        Message message = new Message();
        message.setTitle("沐言科技");
        message.setContent("www.yootk.com");
        return message;
    }
}


4、
package com.yootk.advice;

import com.yootk.vo.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalDataBindAdvice { // 全局数据绑定
    @Autowired
    private Message message; // 注入Message对象实例
    @ModelAttribute(name = "bindModel")
    public Object dataBind() {
        Map<String, Object> map = new HashMap<>(); // 绑定一个Map集合
        map.put("title", "【YOOTK】" + this.message.getTitle());   // 所有的相应都需要提供有此类数据
        map.put("content", "【YOOTK】" + this.message.getContent());   // 所有的相应都需要提供有此类数据
    }
}


5、

package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/data/*") // 添加父路径
public class GlobalDataAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo")
    public Object echo(String message, Model model) {
        Map<String, Object> map = (Map<String, Object>) model.asMap().get("bindModel"); // 获取绑定的Map集合
        map.put("data", "【ECHO】" + message); // 不同的处理有可能会返回不同的数据结果
        return map; // 通过Map返回数据

    }

    @RequestMapping("calc")
    public Object calc(int x, int y, Model model){
        Map<String, Object> map = (Map<String, Object>) model.asMap().get("bindModel"); // 获取绑定的Map集合
        map.put("data", x / y); // 不同的处理有可能会返回不同的数据结果
        return map; // 通过Map返回数据
    }
}

localhost/data/echoMo?message=李兴华高薪就业编程训练营(edu.yootk.com)
localhost/data/calcMo?x=10&y=5

全局数据预处理

数据程序类

  • 在 SprinaBoot 中所有的 Action 业务处理方法,都可以自动的将请求参数自动的设置到匹配的类属性之中,但是在开发中却有可能出现不同类包含有重名属性的问题
1、
package com.yootk.vo;

import lombok.Data;

@Data
public class Company {
    private Long cid;
    private String name;
}

2、
package com.yootk.vo;

import lombok.Data;

@Data
public class Dept {
    private Long deptno;
    private String name;
}

3、
package com.yootk.action;

import com.yootk.vo.Company;
import com.yootk.vo.Dept;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/dept/*")
public class DeptAction {
    @RequestMapping("add")
    public Object add(Company company, Dept dept) {
        Map<String, Object> map = new HashMap<>();
        map.put("dept", dept);
        map.put("company", company);
        return map;
    }
}


4、
localhost/dept/add?cid=101&name=沐言科技&deptno=9090&name=教学研发部

5、
package com.yootk.advice;

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

@ControllerAdvice // 同样的注解,不同的味道
public class GlobalDataPreparemenetAdvice { // 全局数据预处理
    @InitBinder("company")
    public void company(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("company."); // 参数前缀
    }
    @InitBinder("dept")
    public void dept(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("dept."); // 参数前缀
    }
}


6、
package com.yootk.action;

import com.yootk.vo.Company;
import com.yootk.vo.Dept;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/dept/*")
public class DeptAction {
    @RequestMapping("add")
    public Object add(@ModelAttribute("company") Company company,
                      @ModelAttribute("dept") Dept dept) {
        Map<String, Object> map = new HashMap<>();
        map.put("dept", dept);
        map.put("company", company);
        return map;
    }
}


7、
localhost/dept/add?cid=101&company.name=沐言科技&deptno=9090&dept.name=教学研发部

数据验证

数据验证简介

数据校验处理

  • 如果要想保证用户发送的请求可以得到正确的处理,那么就需要对请求的参数进行验证如果用户发送的请求数据符合既定的验证规则,则可以交由 Action 正确处理,反之则应该进行错误信息提示。而为了简化开发者数据校验的处理操作,可以直接使用“JSR303: Bean Validation 规范”实现具体的数据校验操作。
1、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.vo.Message;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo") // 子路径
    public Object echo(Message message) { // 进行请求参数的接收以及请求内容的回应
        Map<String, String> errors = new HashMap<>();
        if (message.getTitle() == null || "".equals(message.getTitle())) {  // 当前的title属性为空
            errors.put("title", "请求提交的title参数内容为空!"); // KEY就是参数名称
        }
        if (message.getContent() == null || "".equals(message.getContent())) {  // 当前的content属性为空
            errors.put("content", "请求提交的content参数内容为空!"); // KEY就是参数名称
        }
        if (message.getPubdate() == null) {  // 当前的pubdate属性为空
            errors.put("pubdate", "请求提交的pubdate参数内容为空!"); // KEY就是参数名称
        }
        if (errors.size() == 0) {   // 当前没有任何的错误
            message.setTitle("【ECHO】" + message.getTitle());
            message.setContent("【ECHO】" + message.getContent());
            return message;
        }
        return errors;
    }
}
// http://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19
// https://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19

2、
package com.yootk.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

public class MessageValidateInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String, String> errors = new HashMap<>();
        if (request.getParameter("title") == null || "".equals(request.getParameter("title"))) {  // 当前的title属性为空
            errors.put("title", "请求提交的title参数内容为空!"); // KEY就是参数名称
        }
        if (request.getParameter("content") == null || "".equals(request.getParameter("title"))) {  // 当前的content属性为空
            errors.put("content", "请求提交的content参数内容为空!"); // KEY就是参数名称
        }
        if (request.getParameter("pubdate") == null) {  // 当前的pubdate属性为空
            errors.put("pubdate", "请求提交的pubdate参数内容为空!"); // KEY就是参数名称
        }
        if (errors.size() == 0) {   // 没有错误信息
            return true;
        } else {    // 在拦截器里面响应rest数据
            // 本次为了简化起见,直接使用SpringBoot内部自带的Jackson组件来将Map集合转为JSON数据
            ObjectMapper objectMapper = new ObjectMapper(); // Jackson组件
            String errorJSONData = objectMapper.writeValueAsString(errors); // 获取错误的JSON数据
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 设置HTTP状态码
            response.getWriter().print(errorJSONData); // 响应错误数据
            return false;   // 不跳转到控制层
        }
    }
}


3、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.vo.Message;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo") // 子路径
    public Object echo(Message message) { // 进行请求参数的接收以及请求内容的回应
        message.setTitle("【ECHO】" + message.getTitle());
        message.setContent("【ECHO】" + message.getContent());
        return message;
    }
}

4、
package com.yootk.config;

import com.yootk.interceptor.MessageValidateInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.getMessageInterceptor()).addPathPatterns("/message/**");
    }
    @Bean
    public HandlerInterceptor getMessageInterceptor() {
        return new MessageValidateInterceptor();
    }
}

JSR303 数据验证规范

1、
// https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator
implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '6.2.0.Final'
// https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator
implementation group: 'org.hibernate.validator', name: 'hibernate-validator', version: '8.0.1.Final'



2、
ext.versions = [    // 定义所有要使用的版本号
        springboot                          : '2.4.3', // SpringBoot版本号
        junit                               : '5.7.1', // 配置JUnit测试工具的版本编号
        junitPlatformLauncher               : '1.7.1',  // JUnit测试工具运行平台版本编号
        lombok                              : '1.18.18', // Lombok插件对应的版本号
        fastjson                            : '1.2.75', // FastJSON组件对应的版本号
        jackson                             : '2.12.2', // 配置Jackson相关依赖库
        itextpdf                            : '5.5.13.2', // PDF文件生成的依赖库
        easypoi                             : '4.3.0', // 生成Excel处理的依赖库
        hibernateValidator                  : '6.2.0.Final', // JSR303验证库
]
ext.libraries = [   // 定义所有的依赖库
        // 以下的配置为SpringBoot项目所需要的核心依赖
        'spring-boot-gradle-plugin': "org.springframework.boot:spring-boot-gradle-plugin:${versions.springboot}",
        // 以下的配置为与项目用例测试有关的依赖
        'junit-jupiter-api': "org.junit.jupiter:junit-jupiter-api:${versions.junit}",
        'junit-vintage-engine': "org.junit.vintage:junit-vintage-engine:${versions.junit}",
        'junit-jupiter-engine': "org.junit.jupiter:junit-jupiter-engine:${versions.junit}",
        'junit-platform-launcher': "org.junit.platform:junit-platform-launcher:${versions.junitPlatformLauncher}",
        'junit-bom': "org.junit:junit-bom:${versions.junit}",
        // 以下的配置为Lombok组件有关的依赖
        'lombok': "org.projectlombok:lombok:${versions.lombok}",
        // 以下的配置为FastJSON组件有关的依赖
        'fastjson': "com.alibaba:fastjson:${versions.fastjson}",
        // 以下的配置为Jackson将输出转换为XML有关的依赖
        'jackson-dataformat-xml': "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${versions.jackson}",
        'jackson-databind': "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}",
        'jackson-annotations': "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}",
        // 以下的配置为ITextPDF输出的有关依赖配置
        'itextpdf': "com.itextpdf:itextpdf:${versions.itextpdf}",
        // 以下的配置为生成Excel文件有关的依赖配置
        'easypoi-spring-boot-starter': "cn.afterturn:easypoi-spring-boot-starter:${versions.easypoi}",
        // 以下的配置为HibernateValidator实现的JSR303验证标准依赖
        'hibernate-validator': "org.hibernate.validator:hibernate-validator:${versions.hibernateValidator}"
]

3、
project('microboot-web') { // 子模块
    dependencies { // 配置子模块依赖
        compile(project(':microboot-common')) // 引入其他子模块
        compile(libraries.'hibernate-validator') // 引入依赖库
    }
}

4、
package com.yootk.vo;

import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data // 只要使用Lombok组件,99%的情况下就使用这一个注解
public class Message {
    @NotBlank // 该字段的内容不允许为空
    private String title; // 字符串的参数
    @NotNull // 该字段的内容不允许为空
    private Date pubdate; // 日期参数
    @NotBlank // 该字段的内容不允许为空
    private String content;
}


5、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.vo.Message;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo") // 子路径
    public Object echo(@Valid Message message) { // 进行请求参数的接收以及请求内容的回应
        message.setTitle("【ECHO】" + message.getTitle());
        message.setContent("【ECHO】" + message.getContent());
        return message;
    }
}

6、
http://localhost/messageJSR303/echo

http://localhost/messageJSR303/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19


7、
package com.yootk.vo;

import lombok.Data;

import javax.validation.constraints.Digits;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data // 只要使用Lombok组件,99%的情况下就使用这一个注解
public class Message {
    @NotBlank // 该字段的内容不允许为空
    private String title; // 字符串的参数
    @NotNull // 该字段的内容不允许为空
    private Date pubdate; // 日期参数
    @NotBlank // 该字段的内容不允许为空
    private String content;
    @Email // 邮箱检测
    @NotBlank
    private String email;
    @Digits(integer = 1, fraction = 0) // 1位整数,0位小数
    private Integer level; // 消息级别
}



http://localhost/messageJSR303/echo?level=99&email=188

8、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.vo.Message;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
@Validated // 启用当前的JSR303注解
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo") // 子路径
    public Object echo(@Valid Message message) { // 进行请求参数的接收以及请求内容的回应
        message.setTitle("【ECHO】" + message.getTitle());
        message.setContent("【ECHO】" + message.getContent());
        return message;
    }
    @RequestMapping("get")
    public Object get(@NotNull @Length(min=5, max=15) String id) {  // 根据id加载数据信息
        return "【YOOTK】" + id;
    }
}
9、
http://localhost/messageJSR303/get

http://localhost/messageJSR303/get?id=lxh

设置错误信息

1、
package com.yootk.vo;

import lombok.Data;

import javax.validation.constraints.Digits;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data // 只要使用Lombok组件,99%的情况下就使用这一个注解
public class Message {
    @NotBlank(message = "消息标题不允许为空!") // 该字段的内容不允许为空
    private String title; // 字符串的参数
    @NotNull(message = "消息发布日期数据不允许为空!") // 该字段的内容不允许为空
    private Date pubdate; // 日期参数
    @NotBlank(message = "消息内容不允许为空!") // 该字段的内容不允许为空
    private String content;
    @Email(message = "消息发送的邮箱格式错误!") // 邮箱检测
    @NotBlank(message = "消息发送的邮箱不允许为空!")
    private String email;
    @Digits(integer = 1, fraction = 0, message = "消息级别只能是一位整数") // 1位整数,0位小数
    private Integer level; // 消息级别
}


2、
http://localhost/messageJSR303/echo



3、
message.title.notblank.error=消息标题不允许为空!
message.email.notblank.error=消息发送邮箱不允许为空!
message.email.email.error=消息发送邮箱格式错误!
message.pubdate.notnull.error=消息发送日期不允许为空
message.content.notblank.error=消息的内容不允许为空!
message.level.digits.error=消息级别必须是1位数字!

4、

package com.yootk.vo;

import lombok.Data;

import javax.validation.constraints.Digits;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data // 只要使用Lombok组件,99%的情况下就使用这一个注解
public class Message {
    @NotBlank(message = "{message.title.notblank.error}") // 该字段的内容不允许为空
    private String title; // 字符串的参数
    @NotNull(message = "{message.pubdate.notnull.error}") // 该字段的内容不允许为空
    private Date pubdate; // 日期参数
    @NotBlank(message = "{message.content.notblank.error}") // 该字段的内容不允许为空
    private String content;
    @Email(message = "{message.email.email.error}") // 邮箱检测
    @NotBlank(message = "{message.email.notblank.error}")
    private String email;
    @Digits(integer = 1, fraction = 0, message = "{message.level.digits.error}") // 1位整数,0位小数
    private Integer level; // 消息级别
}

自定义验证器

自定义验证器

  • 用户自定义验证器处理时,一般都需要定义有一个专属的验证器注解,同时还需要为该注解定义有一个具体的验证处理类,考虑到该验证器有可能多个项目中都会被使用到,所以本次将在“microboot-common”模块中编写一个基于正则表达式实现的验证器
1、
package com.yootk.common.validation.annotation;

import com.yootk.common.validation.annotation.handler.RegexConstraintValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Target({ElementType.FIELD, ElementType.PARAMETER}) // 该注解允许在成员和参数上使用
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = RegexConstraintValidator.class) // 正则处理类
public @interface RegexValidator { // 自定义正则注解
    // 所有的验证注解里面需要提供有三个核心的属性内容:message、groups、payload
    String message() default "数据正则验证错误"; // 错误信息
    Class<?>[] groups() default { }; // 验证分组
    Class<? extends Payload>[] payload() default { }; // 附加源数据信息
    String pattern(); // 自定义属性,接收要使用的正则表达式
}


2、
package com.yootk.common.validation.annotation.handler;

import com.yootk.common.validation.annotation.RegexValidator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class RegexConstraintValidator implements ConstraintValidator<RegexValidator, Object> { // 处理类
    private String regexExpression; // 保存正则表达式
    @Override
    public void initialize(RegexValidator constraintAnnotation) {
        this.regexExpression = constraintAnnotation.pattern(); // 通过注解配置的内容获取表达式信息
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null) { // 数据不允许为空
            return false;
        }
        return value.toString().matches(this.regexExpression);
    }
}


3、
message.flag.notblank.error=消息标记不允许为空!
message.flag.regex.error=消息标记匹配格式错误!

4、
package com.yootk.vo;

import com.yootk.common.validation.annotation.RegexValidator; // 自定义的验证规则
import lombok.Data;

import javax.validation.constraints.Digits;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data // 只要使用Lombok组件,99%的情况下就使用这一个注解
public class Message {
    @NotBlank(message = "{message.title.notblank.error}") // 该字段的内容不允许为空
    private String title; // 字符串的参数
    @NotNull(message = "{message.pubdate.notnull.error}") // 该字段的内容不允许为空
    private Date pubdate; // 日期参数
    @NotBlank(message = "{message.content.notblank.error}") // 该字段的内容不允许为空
    private String content;
    @Email(message = "{message.email.email.error}") // 邮箱检测
    @NotBlank(message = "{message.email.notblank.error}")
    private String email;
    @Digits(integer = 1, fraction = 0, message = "{message.level.digits.error}") // 1位整数,0位小数
    private Integer level; // 消息级别
    @NotBlank(message = "{message.flag.notblank.error}")
    @RegexValidator(pattern = "[a-zA-Z]{1,5}-\\d{1,3}")
    private String flag; // 追加字段,例如:yootk-101
}

5、
http://localhost/messageJSR303/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19&email=muyan@yootk.com&level=1&flag=happy
http://localhost/messageJSR303/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19&email=muyan@yootk.com&level=1&flag=yootk-101

demo


上次编辑于: