赞
踩
目录
2.5.2.2、使用CharacterEncodingFilter解决POST乱码请求
Spring MVC(Model-View-Controller)是一个基于Java的Web框架,它是Spring框架的一部分,用于开发Web应用程序。Spring MVC提供了一种灵活的模型-视图-控制器(MVC)体系结构,用于开发Web应用程序。它使用基于注释的配置和声明性模板来减少开发时间,并以模块化的方式提供了许多功能,包括数据绑定、数据验证、视图解析和异常处理等。
java SpringMVC的工程结构一般来说分为三层,自下而上是Modle层(模型,数据访问层)、Cotroller层(控制,逻辑控制层)、View层(视图,页面显示层),其中Modle层分为两层:dao层、service层,MVC架构分层的主要作用是解耦。采用分层架构的好处,普遍接受的是系统分层有利于系统的维护,系统的扩展。就是增强系统的可维护性和可扩展性。
对于Spring这样的框架,(View\Web)表示层调用控制层(Controller),控制层调用业务层(Service),业务层调用数据访问层(Dao)
service层:业务层,用来实现业务逻辑。能调用dao层或者service层,返回数据对象DO或者业务对象BO,BO通常由DO转化、整合而来,可以包含多个DO的属性,也可以是只包含一个DO的部分属性。通常为了简便,如果无需转化,service也可以直接返回DO。外部调用(HTTP、RPC)方法也在这一层,对于外部调用来说,service一般会将外部调用返回的DTO转化为BO。是专注业务逻辑,对于其中需要的数据库操作,都通过Dao去实现。主要去负责一些业务处理,比如取得连接、关闭数据库连接、事务回滚,一些复杂的逻辑业务处理就放到service层。
DAO层:负责访问数据库进行数据的操作,取得结果集,之后将结果集中的数据取出封装到VO类对象之后返回给service层。数据层,直接进行数据库的读写操作,返回数据对象DO,DO与数据库表一一对应。Dao的作用是封装对数据库的访问:增删改查,不涉及业务逻辑,只是达到按某个条件获得指定数据的要求。
Cotroller层:叫做控制层,主要的功能是处理用户发送的请求。主要处理外部请求。调用service层,将service层返回的BO/DO转化为DTO/VO并封装成统一返回对象返回给调用方。如果返回数据用于前端模版渲染则返回VO,否则一般返回DTO。不论是DTO还是VO,一般都会对BO/DO中的数据进行一些转化和整合。
View层:叫做显示层,主要是负责现实数据。
依赖就忽略了
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
-
- <!-- 1. 开启注解扫描-->
- <context:component-scan base-package="com.lin.controller"/>
- <!-- 2. 配置处理器映射器-->
- <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />-->
- <!-- 3. 开启处理器适配器-->
- <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />-->
- <!-- 上面两段配置被下面的一句话所替代(封装)-->
- <mvc:annotation-driven />
- <!-- 4. 开启视图解析器-->
- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/"/>
- <property name="suffix" value=".jsp"/>
- </bean>
- </beans>
- <!DOCTYPE web-app PUBLIC
- "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
- "http://java.sun.com/dtd/web-app_2_3.dtd" >
-
- <web-app>
- <display-name>Archetype Created Web Application</display-name>
-
- <!-- 配置springmvc的核心servlet-->
- <servlet>
- <servlet-name>springmvc</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <!-- 告诉springmvc配置文件的位置-->
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:springmvc.xml</param-value>
- </init-param>
- </servlet>
- <servlet-mapping>
- <!-- 拦截所有请求-->
- <servlet-name>springmvc</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- </web-app>
- package com.lin.controller;
-
- import org.apache.ibatis.annotations.Param;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- /**
- * @author XiaoLin
- * @date 2021/2/17 17:09
- */
- @Controller
- public class HellowController {
-
- /**
- * @Description:第一个springmvc测试类
- * @author XiaoLin
- * @date 2021/2/17
- * @Param: [username, password]
- * @return java.lang.String
- */
- /*
- RequestMapping的修饰范围:可以用在类上和方法上,他的作用如下:
- 1. 用在方法上可以给当前方法加入指定的请求路径
- 2. 用在类上可以给类中的所有方法都加入一个统一的请求路径,在这个方法访问之前都必须加上
- */
- @RequestMapping("/hello")
- public String hello(String username,String password){
- System.out.println("hello");
- return "index";
- }
- }
该注解用来类上标识这是一个控制器组件类并创建这个类实例,告诉spring我是一个控制器。
这个注解可以作用在方法上或者是类上,用来指定请求路径。
传统的Servlet开发跳转方式有两种:
通过测试我们可以发现,SpringMVC默认的就是使用请求转发的方式来进行跳转到前台页面的;
- @Controller
- @RequestMapping("forwoartAndRedirect")
- public class TestForwoartAndRedirect {
-
- @RequestMapping("test")
- public String test(){
- System.out.println("test");
- return"index";
- }
- }
如果我们想使用重定向的方式来进行跳转的话,需要使用SpringMVC提供给我们的关键字——redirect:来完成。
语法:return "redirect:/视图全路径名";
**注意:**在redirect:后接页面的不是逻辑名,而是全路径名。因为redirect跳转不会经过视图解析器。
如果我们想使用请求转发的方式跳转到相同(不同)Controller的不同方法的时候,我们也需要使用SpringMVC提供的关键字:forward:。
语法:return:"forward: /需要跳转的类上的@RequestMapping的值/需要跳转的方法上的@RequestMapping的值;"
如果我们想使用重定向的方式跳转到相同(不同)Controller的不同方法的时候,我们也需要使用SpringMVC提供的关键字:redirect:。
语法:return:"redirect: /需要跳转的类上的@RequestMapping的值/需要跳转的方法上的@RequestMapping的值;"
在传统的Servlet开发,我们一般都是用这种方式来进行接收请求参数的。
- // 接收名字为name的参数
- request.getParameter(name)
他有几个需要注意的点:
SpringMVC使用的是控制器中方法形参列表来接收客户端的请求参数,他可以进行自动类型转换,要求传递参数的key要与对应方法的形参变量名一致才可以完成自动赋值。他的优势很明显:
要求传递参数的key要与对应方法的形参变量名一致才可以完成自动赋值。
如果我们需要接收对象类型的话,直接将需要接收的对象作为控制器的方法参数声明即可。SpringMVC会自动封装对象,若传递参数key与对象中属性名一致,就会自动封装成对象。
如果我们需要接收数组类型的时候,只需将要接收的数组类型直接声明为方法的形式参数即可。
SpringMVC不能直接通过形式参数列表的方式接收集合类型的参数,如果需要接收集合类型的参数必须将集合放入一个对象中,并且提供get/set方法,才可以。推荐放入VO对象中进行封装,进而使用对象类型来进行接收。
GET请求方式出现乱码需要分Tomcat版本进行讨论:
SpringMVC中默认没有对POST请求进行任何编码处理,所以无论什么版本直接接收POST请求都会出现中文乱码。
在Servlet阶段,我们学过过滤器,我们可以自定义过滤器来进行过滤编码。
- package com.filter;
-
- import javax.servlet.*;
- import java.io.IOException;
-
- //自定义编码filter
- public class CharacterEncodingFilter implements Filter {
-
- private String encoding;
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- this.encoding = filterConfig.getInitParameter("encoding");
- System.out.println(encoding);
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- request.setCharacterEncoding(encoding);
- response.setCharacterEncoding(encoding);
- chain.doFilter(request,response);
- }
-
- @Override
- public void destroy() {
-
- }
- }
- <!--配置post请求方式中文乱码的Filter-->
- <filter>
- <filter-name>charset</filter-name>
- <filter-class>com.filter.CharacterEncodingFilter</filter-class>
- <init-param>
- <param-name>encoding</param-name>
- <param-value>UTF-8</param-value>
- </init-param>
- </filter>
-
- <filter-mapping>
- <filter-name>charset</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!--配置post请求方式中文乱码的Filter-->
- <filter>
- <filter-name>charset</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>
- </filter>
-
- <filter-mapping>
- <filter-name>charset</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- package com.filter;
-
- import javax.servlet.*;
- import java.io.IOException;
-
- //自定义编码filter
- public class CharacterEncodingFilter implements Filter {
-
- private String encoding;
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- this.encoding = filterConfig.getInitParameter("encoding");
- System.out.println(encoding);
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- request.setCharacterEncoding(encoding);
- response.setCharacterEncoding(encoding);
- chain.doFilter(request,response);
- }
-
- @Override
- public void destroy() {
-
- }
- }
数据传递机制主要包含三个问题:
在以前的Servlet开发中,我们一般是将数据放入作用域(request、session、application),如果数据是单个的直接用EL表达式在前端进行展示,如果是集合或者数组,可以用EL表达式➕JSTL标签进行遍历后在前端进行展示。
几次请求 | 地址栏 | WEB-INF中资源 | 共享请求数据 | 有无表单重复提交 | |
请求转发 | 1次 | 不改变 | 可以访问 | 可以共享 | 有 |
重定向 | 多次 | 改变 | 不可访问 | 不可共享 | 无 |
加上forward 关键字,表示请求转发,相当于
request.getRequestDispatcher().forward(request,response),转发后浏览器地址栏不变,共享之前请求中的数据。加了关键字后,配置的视图解析器就不起作用了。如果返回视图必须写全路径
- package cn.linstudy.web.controller;
- @Controller
- public class ResponseController {
-
- @RequestMapping("/TestForward")
- public String forward() {
- return "forward:/WEB-INF/views/welcome.jsp";
- }
- }
加上 redirect 关键字,表示重定向,相当于 response.sendRedirect(),重定向后浏览器地址栏变为重定向后的地址,不共享之前请求的数据。
- package cn.linstudy.web.controller;
- @Controller
- public class ResponseController {
- // localhost/r
- @RequestMapping("/TestRedirect")
- public String redirect() {
- return "redirect:/static/demo.html";
- }
- }
在请求转发和重定向的时候,我们一般有两种方式来写请求路径:
回顾之前使用 Servlet3.0 来解决文件上传的问题,编写上传表单(POST、multipart/form-data),还在处理方法 doPost 中编写解析上传文件的代码。但是在SpringMVC是可以帮我们简化文件上传的步骤和代码。
注意请求数据类型必须是:multipart/form-data,且请求方式是POST。
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>文件上传</title>
- </head>
- <body>
- <form action="/upload" method="POST" enctype="multipart/form-data">
- 文件:<input type="file" name="pic"><br>
- <input type="submit" value="提交"/>
- </form>
- </body>
- </html>
我们可以在web.xml中指定上传文件的大小。
- <servlet>
- <servlet-name>dispatcherServlet</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:mvc.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- <multipart-config>
- <max-file-size>52428800</max-file-size>
- <max-request-size>52428800</max-request-size>
- </multipart-config>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcherServlet</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
在mvc.xml中配置上传解析器,使用springmvc中multipartfile接收客户端上传的文件必须配置文件上传解析器且解析的id必须为multipartResolver
- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
- <!--控制文件上传大小单位字节 默认没有大小限制 这里是2-->
- <property name="maxUploadSize" value="2097152"/>
- </bean>
- package cn.linstudy.controller;
- @Controller
- public class UploadController {
- // Spring 容器存在 ServletContext 类型的对象,所以定义好 ServletContext 类型字段贴@Autowired 注解即可获取到
- @Autowired
- private ServletContext servletContext;
- @RequestMapping("/upload")
- public ModelAndView upload(Part pic) throws Exception {
- System.out.println(pic.getContentType()); // 文件类型
- System.out.println(pic.getName()); // 文件参数名
- System.out.println(pic.getSize()); // 文件大小
- System.out.println(pic.getInputStream()); // 文件输入流
- // FileCopyUtils.copy(in, out),一个 Spring 提供的拷贝方法
- // 获取项目 webapp 目录下 uploadDir 目录的绝对路径
- System.out.println(servletContext.getRealPath("/uploadDir"));
- return null;
- }
- }
文件下载:将服务器上的文件下载到当前用户访问的计算机的过程称之为文件下载
下载时必须设置响应的头信息,指定文件以何种方式保存,另外下载文件的控制器不能存在返回值,代表响应只用来下载文件信息`
- /**
- * 测试文件下载
- * @param fileName 要下载文件名
- * @return
- */
- @RequestMapping("download")
- public String download(String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {
- //获取下载服务器上文件的绝对路径
- String realPath = request.getSession().getServletContext().getRealPath("/down");
- //根据文件名获取服务上指定文件
- FileInputStream is = new FileInputStream(new File(realPath, fileName));
- //获取响应对象设置响应头信息
- response.setHeader("content-disposition","attachment;fileName="+ URLEncoder.encode(fileName,"UTF-8"));
- ServletOutputStream os = response.getOutputStream();
- IOUtils.copy(is,os);
- IOUtils.closeQuietly(is);
- IOUtils.closeQuietly(os);
- return null;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。