SpringMVC框架学习笔记(四)(AJAX数据传递,数据转换,拦截器,异常,封装)

在这里插入图片描述

  1. 引入jquery.js
  2. 在核心配置文件中设置静态资源映射路径
 <!--方式二:静态资源映射路径-->
    <mvc:resources mapping="/js/**" location="/resources/js/"/>

十、AJAX数据传递

10.1 JSON数据传递

自己处理数据

processData : false : 对data数据不做处理
在这里插入图片描述

  • @RequestBody的作用是将前端传来的json格式的数据转为自己定义好的javabean对象
  • @ResponseBody的作用是将后端以return返回的javabean类型数据转为json类型数据

在此例中,demo用@RequestBody将传递过来的json(JS转换而来)转换为studentjava对象,再用@ResponseBody将其转换为json响应回去.
类似于前文初学Ajax
前端代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <base href="${pageContext.request.contextPath}/">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        学生姓名:<input type="text" id="student_name"><br/>
        学生健康:<input type="radio" name="flag" value="true">健康<input type="radio" name="flag" value="false" checked>不健康<br/>
        学生年龄:<input type="text" id="age"><br/>
        学生爱好:
        <select id="hobby" style="width: 150px;height: 150px" multiple>
            <option value="100">篮球</option>
            <option value="200">足球</option>
            <option value="300">排球</option>
        </select>
        <br/>
        班级名称:<input type="text" id="class_name"><br/>
        班级人数:<input type="text" id="number"><br/>
        <button>提交</button>
        <script src="js/jquery-1.12.4.min.js"></script><%--读取映射路径--%>
        <script>
           $(function (){
               $("button").click(function (){
                   let student_name = $.trim($("#student_name").val());
                   let flag = $("input[name='flag']:checked").val();
                   let age = $.trim($("#age").val());
                   let class_name = $.trim($("#class_name").val());
                   let number = $.trim($("#number").val());
                   /*转换为数组*/
                   let hobby = $.map($.makeArray($("#hobby option:selected")), function (obj) {
                       return $(obj).val();
                   });
                   console.log(hobby);
                   //将数据封成JavaScript对象,并且JS属性和类中属性保存一致:规则
                   let studentObj = {
                       studentName:student_name,
                       studentFlag:flag,
                       studentAge:age,
                       hobbyArray:hobby,
                       classInfo:{//关联对象
                           className:class_name,
                           number:number
                       }
                   };
                   console.log("JS对象",studentObj);
                   //将JS对象转换为JSON的数据格式
                   let jsonStr = JSON.stringify(studentObj);
                   console.log("JSON格式",jsonStr);
                   $.ajax({
                       type:"post",
                       url:"json/data01",
                       data:jsonStr,
                       contentType:"application/json;charset=UTF-8",
                       processData:false,
                       dataType:"json",
                       success:function(response){
                           console.log("JS对象,通过dataType将JSON格式转换为JS对象 ",response);
                       }
                   });

               });

           });
        </script>
    </body>
</html>

后端代码:

@Controller
@RequestMapping("/json")
public class JSONController {
    @PostMapping("/data01")
    @ResponseBody
    public Student test01(@RequestBody Student student){
        System.out.println("student = " + student);
        student.setStudentName("夜雨");
        return student;
    }
}

控制台输出:
在这里插入图片描述
在这里插入图片描述

引入jquery.serializejson.js,用serializeJSON方法序列化表单

上文过于烦琐,于是引入jquery.serializejson.js文件:<script src="js/jquery.serializejson.min.js"></script><%--读取映射路径--%>
需要和类中name属性对应,故将jsp文件改为表单
注意,表示数组时,在name属性中应加上[]

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <base href="${pageContext.request.contextPath}/">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form id="JsonFrom">
            学生姓名:<input type="text" name="studentName"><br/>
            学生健康:<input type="radio" name="studentHealth" value="true">健康<input type="radio" name="studentHealth" value="false" checked>不健康<br/>
            学生年龄:<input type="text" name="studentAge"><br/>
            学生爱好:
            <select name="hobbyArray[]" style="width: 150px;height: 150px" multiple>
                <option value="100">篮球</option>
                <option value="200">足球</option>
                <option value="300">排球</option>
            </select>
            <br/>
            班级名称:<input type="text" name="classInfo[className]"><br/>
            班级人数:<input type="text" name="classInfo[number]"><br/>
            <button>提交</button>
        </form>
        <script src="js/jquery-1.12.4.min.js"></script><%--读取映射路径--%>
        <script src="js/jquery.serializejson.min.js"></script><%--读取映射路径--%>
        <script>
           $(function (){
               $("#JsonFrom").submit(function (){
                   let studentObj = $('#JsonFrom').serializeJSON();//表单进行序列化
                   console.log("JS对象",studentObj);
                   //将JS对象转换为JSON的数据格式
                   let jsonStr = JSON.stringify(studentObj);
                   console.log("JSON格式",jsonStr);
                   $.ajax({
                       type:"post",
                       url:"json/data01",
                       data:jsonStr,
                       contentType:"application/json;charset=UTF-8",
                       processData:false,
                       dataType:"json",
                       success:function(response){
                           console.log("JS对象,通过dataType将JSON格式转换为JS对象 ",response);
                       }
                   });
                   return false;//阻止默认自动提交表单行为
               });

           });
        </script>
    </body>
</html>

在这里插入图片描述

10.2 FormData数据传递

不需要name属性,因此表单设有id属性即可
前端代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <base href="${pageContext.request.contextPath}/">
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form id="uploadForm">
        普通的控件:<input type="text" id="name"><br/>
        文件1<input type="file" id="file"><br/>
        <button>文件上传</button>
    </form>
    <script src="js/jquery-1.12.4.min.js"></script>
    <script>
        $("#uploadForm").submit(function () {
            //创建表单对象,用来存储数据
            let formData = new FormData();
            formData.append("userName",$("#name").val());
            formData.append("myFile",$("#file").prop("files")[0]);
            $.ajax({
                type:"post",
                url:"json/upload",
                data:formData,
                contentType:false,//设置默认使用multipart/form-data
                processData:false,
                success:function(response){
                    console.log(response);
                }
            });

            return false;
        });
    </script>
    </body>
</html>

后端代码

 @Autowired
    private ServletContext appliaction;
    @PostMapping("/upload")
    @ResponseBody
    public String upload04(String userName, MultipartFile myFile, Model model) throws IOException {
        //1.获取服务器上传文件的所在目录
        String path = appliaction.getRealPath("/attr/");
        //2.跟磁盘建立联系
        File folder = new File(path);
        if(!folder.exists()){//判断目录是否存在
            folder.mkdirs();//创建文件夹
        }
        //3.获取上传文件的名称
        String fileName = myFile.getOriginalFilename();
        model.addAttribute("showName",fileName);//元素内容
        //4.获取上传文件名称的后缀名称
        String ext = FilenameUtils.getExtension(fileName);
        //5.创建新的文件名称(唯一)
        String newFileName = getUniqueFileName()+"."+ext;
        model.addAttribute("serverName",newFileName);//href属性中
        //6.文件上传(不做验证,因为在前端可以设置验证)
        myFile.transferTo(new File(path+"/"+newFileName));

        return newFileName;
    }
    public static String getUniqueFileName(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

结果:
在这里插入图片描述

十一、数据转换

HTML页面中传递的数据(除文件上传外),普通的表单提交的数据后台默认接收的类型都是String或者String[],SpringMVC提供了内置转换器将其转换成对应的数据类型,

接下来以java.util.Date类型来举例:当客户端传递的是字符串是否可以转换成功?

 <form action="data01" method="get">
      姓名:<input type="text" name="userName"/><br/>
      生日:<input type="text" name="birthday"/><br/>
      <button>提交数据</button>
    </form>
@Controller
public class UserController {
    @GetMapping("/data01")
    @ResponseBody
    public void test01(String userName, Date birthday, User user){
        System.out.println("userName = " + userName);
        System.out.println("birthday = " + birthday);
        System.out.println("user = " + user);
    }

测试数据:http://127.0.0.1:8080/mvc/data01?userName=夜雨8&birthday=2021-08-08
报错:HTTP ERROR 400

测试数据http://127.0.0.1:8080/mvc/data01?userName=夜雨8&birthday=2021/08/08 12:13:14
测试成功:
控制台:在这里插入图片描述

默认支持String To Date : yyyy/MM/dd或者yyyy/MM/dd HH:mm:ss等组合方式,一般情况下在做开发的时候,我们需要约定一固定的数据格式(类似日期格式)
当此例中将get请求改为post请求后依旧可以成功运行,只不过这个时候传递中文有可能在tomcat下出现中文乱码

所以我们此时应该配置POST请求的中文乱码过滤器:

 <!--配置POST请求的中文乱码过滤器-->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

(1)针对于数字和日期,SpringMVC提供格式化注解,方便我们设置规则

<form action="data01" method="post">
    姓名:<input type="text" name="userName"/><br/>
    生日:<input type="text" name="birthday"/><br/>
    创建日期:<input type="text" name="myDate"/><br/>
    <button>提交数据</button>
</form>
  • @DateTimeFormat(pattern = "yyyy-MM-dd")作用:设置日期格式
@Controller
public class UserController {
	@PostMapping("/data01")
	@ResponseBody
	public void test01(String userName,
				@RequestParam("myDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date createDate,
					   User user){
		System.out.println("userName = " + userName);
		System.out.println("createDate = " + createDate);
		System.out.println("user = " + user);
	}
}

在这里插入图片描述

(2)@InitBinder自定义转换规则

 @InitBinder
        public void InitBinder(WebDataBinder binder){
            System.out.println("............."+Math.random());
            binder.registerCustomEditor(Date.class, new PropertyEditorSupport(){//注册,自定义绑定规则:当数据为date类型的绑定规则
                @Override
                public void setAsText(String text) throws IllegalArgumentException {
                    System.out.println("text获取客户端数据 = " + text);
                    DateFormat df = new SimpleDateFormat("yyy-MM-dd");//date格式
                    Date date = null;
                    try {
                        date = df.parse(text);//转换格式
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                    setValue(date);//将转换后的数据赋值给相应形参
                }
            });
        }

在这里插入图片描述

(3) 全局的格式化处理(引入lang3工具包)

在pom.xml中引入lang3工具包

    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.12.0</version>
    </dependency>
  • StringToDateFormatter工具类
package yue.formatter;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.format.Formatter;

import java.text.ParseException;
import java.util.Date;
import java.util.Locale;

public class StringToDateFormatter implements Formatter<Date> {
    private final static String[] parsePatterns = {//预先定义一些格式
            "yyy-MM-dd HH:mm:ss","yyy-MM-dd",
            "yyy/MM/dd HH:mm:ss","yyy/MM/dd",
            "yyy.MM.dd HH:mm:ss","yyy.MM.dd",
            "yyyMMddHHmmss","yyyMMdd"
    };
    @Override
    public Date parse(String s, Locale locale) throws ParseException {//格式化
        return DateUtils.parseDate(s, parsePatterns);//支持可变参数
    }

    @Override
    public String print(Date date, Locale locale) {//打印
        return DateFormatUtils.format(date, "yyy-MM-ss HH:mm:ss");
    }
}

然后在核心配置文件中进行配置;


 <!--全局格式化处理-->
    <bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="conversionService">
        <property name="formatters">
            <set>
                <bean class="yue.formatter.StringToDateFormatter"/>
            </set>
        </property>
    </bean>

在这里插入图片描述
结果:
在这里插入图片描述
四种格式都转换成功!

十二、拦截器

概述

顾名思义:拦截器就是对资源进行拦截,可以根据自己的需求设置拦截规则
是过滤器的一种特殊情况

12.1 过滤器(复习)

在Java Web学习阶段,学过三大组件:Servlet,Filter,Listener

过滤器,可以根据自己需求设置过滤规则,不管是JSP,静态资源回收映射路径控制器都在过滤器范围内,如果有多个过滤器,那么它们之间组成过链,过滤器的过程一定要掌握(应届生会考)
过滤器两种方式:xml和注解

@WebFilter(name="loginFilter",value={"/sys/*","*.jsp"})//注册和设置过滤规则

public class LoginFilter implements java.servlet.Filter{
	public LoginFilter (){};//1.过滤器在度武器启动的时候就对其进行实例化操作
	public void init(){};//2.实例化完成后立即执行初始化操作
	public void destroy(){};//3.过滤器的销毁,在服务器关闭时调用
	
	/*过滤器资源访问*/
	public void doFilter(ServletRequest req,ServletRsponse resp,FIlterChain chain){
	HttpServletRequest request = (HttpServletRequest)req;//强制类型转换,对象下转型
	HttpServletResponse response = (HttpServletResponse)resp;

	/*获取请求的路径*/
	String path = request.getResourceURI();
	if(path,endsWidth(".js") || path,endsWidth("/sys/login")){//排除该资源,不进行过滤
	response.sendRedirect(request.getContextPath()+"/sys/login");//返回登录页面
	}else{
		chain.doFilter(request,response);//访问下一个过滤器或者资源
	}
	}
}

12.2 拦截器(Interceptor)

拦截器可以设置自己的拦截规则:拦截的是资源的访问路径(映射路径或者静态资源),但是对JSP不会拦截.
所以概括起来:拦截器只会对控制器(处理器)进行拦截.

(1) 一个普通的Java类实现了HandlerInterceptor接口,称之为拦截器
  • 拦截器代码:
public class FirstInterceptor implements HandlerInterceptor {
	public FirstInterceptor(){
		System. out . println("拦截器的实例化...irstInterceptor");
	}
	@Override :
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 0bject handler) throws Exception {
		System. out . println("FirstInterceptor.. .1...preHandle==>");
		//return false;//不 允许继续范国下一个拦截器或者资源
		return true;//允许访问下一个拦截器或者资源( chain. doFilter ) 
	}
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, 0bject handler, ModelAndView mode LAndView){
		System. out . println("FirstInterceptor...2... postHandle==>");
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
	System. out .println("FirstInterceptor ...3...afterCcompletion==>"); 

(2) 注册拦截器
<!-- 定义拦截器链-->
<MVC: interceptors>
	<!-- 注册拦截器和设置拦截规则-->
	<MVC: interceptor>
		<MvC :mapping path=" /user/**"/>
		<MvC :mapping path="/student/**"/>
		<bean class="com .yue.interceptor.FirstInterceptor"/>
	</mvC :interceptor>
</mvC: interceptors>

  • 控制器代码
public class Demo01Controller {
	@GetMapping(" /user/add/insert")
	public String test01(){
		System . out .printIn("test01方法.... ");
		return "result";
	}
	@GetMapping("/student/update")
		public String test02(){
		return "result";
	@GetMapping("/teacher/delete")
		public String test03(){
		return "result";
}

  • JSP代码
<body>
	<%.
	System . out. pr intln(Math.random()+"JSP页面");
	>
	显示页面<*=Math. random()>
</body>

结果:

在这里插入图片描述

FirstInterceptor...1...preHandle==>在访问控制器之前执行,如果没有下一个拦截器,范围资源
test01方法....访问的资源
FirstInterceptor ...2... postHandle==>在找到正确视图的视图之前,执行postHandle
0.39211802104708704JSP页面==>访问视图资源
FirstInterceptor ...3... afterCompletion==>当整个视图加载完毕之后,执行afterCompletion

注意1:

只有在找到正确视图的视图之前,执行postHandle
如果出错 如:加入int num = 10/0错误代码
那么就会报错 无法加载到 postHandle
在这里插入图片描述

注意2:

重新定义一个拦截器形成拦截器链后的结果:
在这里插入图片描述

流程图:
在这里插入图片描述

设置不拦截路径<mvc :exclude -mapping path=" /student/update"/>

设置在注册拦截链中,设置了之后拦截器将对此路径不在拦截

注意3:

若设置<MvC :mapping path=" /**"/> 则拦截器拦截所有路径

十三. 异常处理

异常的根Throwable类

  • 错误(Error ) :程序员不可控制
  • 异常( Exception ) :程需要可以控制
    • check异常(编译时异常Exception ) :必须处理
      • 抛出异常:方法后使用throws定义多个抛出异常类
      • 捕获异常: try… .catch ,也可以用其控制流程
    • uncheck异常(运行时异常RuntimeException ) :可以根据自己的情况是否进行相应的处理,如果不处理可以交给默
      认的处理方式( JVM处理)
  • 手动抛出异常:通过自己的捕获可以控制流程
  • 捕获的推荐:尽量将编译时异常转换为运行时异常

13.2 异常的处理方式

(1) web.xml部署描述符可以对异常进行处理
@Controller
Reques tMapping( " /exception")
public class Exception01Controller {
	@GetMapping("/demo01" )
	public String test01(){
		System. out. pr intln("test01方法.... ");
		String s = null;
		System. out. pr intln(s.length());//运行时异常
		return "result";
	}
	@GetMapping( "/demo02")
	public String test02() throws FileNotFoundException {//编译时异常
		System. out. println("test02方法### ");
		InputStream in = new FileInputStream( name: "D:/abc. txt");
		return "result";
	}
	@GetMapping("/demo03")
	public String test03() throws ClassNotFoundException {
		System. out. println("test03方法QQQQQ ");
		Class.forName("a.b.c.d.User");
		return "result";
	}
}

  • web.xml文件异常处理
<error-page>
	<exception-type>java. lang. NullPointerException</exception-type>
	<location> /WEB-INF/view/error1.jsp</location>
</error-page>
<error-page>
	<exception-type>java. io.FileNotFoundException</exception-type>
	<location>/WEB-INF/view/error1.jsp</location>
</error-page>
<error-page>
	<error-code>500</error-code>
	<location>/WEB-INF/view/error.jsp</location>
</error-page>

结果:

访问demo01:在这里插入图片描述
访问demo02:在这里插入图片描述
访问demo03:在这里插入图片描述

(2)全局:简单统异常解析器(第三优先级)

当我们发现程序出现可以被控制的异常的时候,会被该解析器处理。

在核心配置文件中配置

<bean class="org. springframework . web . servlet.hand Ler .Simp LeMappingExceptionResolver">
	<!--当发现的是未知错误,没有设置错误映射的时候,给定的默认页面-->
	<property name="defaultErrorView" value="error"/><!-- 请求转换/WEB- -INF/jsp/error.jsp -- >
	<!--传递错误的信息- ->
	<property name="exceptionAttribute" value="ex" /><!-- request. setAttribute("ex",异常对象) -- ->
	<!--定义异常映射的错误页面-- ->
	<property name=" exceptionMappings">
		<props>
		<prop key="java. io .Fi leNotFoundException">aa</prop>
		<prop key="java. lang. NullPointerException">bb</prop>
		</props>
	</property>
</bean>

(3) 局部注解处理异常(优先级最高)

@ExceptionHandler({FileNotFoundException. class})
public String m1(Exception ex, Model model){
	model. addAttribute( s: "message", ex. getMessage());
	return "cc";
}
@ExceptionHandler({NullPointerException.class})
@ResponseBody
public NullPointerException m1(NullPointerException ex){
	return ex;
}

(4)个人喜欢方式:可以使用@ControllerAdivce@ExceptionHandler 组合自定义的全局异常处理(第二优先级)

@ControllerAdvice
public class ExceptionController {
	@ExceptionHandler({FileNotFoundException. class})
	public String m1(Exception ex,
	, Model model){
		mode l. addAttribute( S: "message",ex. getMessage();
		return "cC";
}
@ExceptionHandler({NullPointerException.class})
@ResponseBody
	public NullPointerException m1(NullPointerException ex){
	return ex;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月色夜雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值