Spring MVC 笔记 (2.5w字讲解)
SpringMVC:
1、MVC理论基础
2、配置环境&搭建项目
3、Controller控制器
4、Interceptor拦截器
5、异常处理
6、JSON数据格式与Axios请求
7、实现文件上传和下载
*8、解读DispatcherServlet源码
一、MVC理论基础
MVC详细解释如下:
- M是指业务模型(Model):通俗的讲就是我们之前用于封装数据传递的实体类。
- V是指用户界面(View):一般指的是前端页面。
- C则是控制器(Controller):控制器就相当于Servlet的基本功能,处理请求,返回响应。
二、配置环境&搭建项目
Maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.10</version>
</dependency>传统xml配置
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 指定创建在类路径下的application.xml配置文件 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 需要先引入context命名空间,然后直接配置base-package属性就可以了 -->
<context:component-scan base-package="com.example"/>
</beans>Controller
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/")
public String hello(){
return "HelloWorld!";
}
}全注解配置
Tomcat会在类路径中查找实现ServletContainerInitializer 接口的类,如果发现的话,就用它来配置Servlet容器
public class MainInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{WebConfiguration.class}; //基本的Spring配置类,一般用于业务层配置
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0]; //配置DispatcherServlet的配置类、主要用于Controller等配置,这里为了教学简单,就不分这么详细了,只使用上面的基本配置类
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; //匹配路径,与上面一致
}
}配置类:
@Configuration
@EnableWebMvc //快速配置SpringMvc注解
@ComponentScan("com.example.controller")
public class WebConfiguration {
}日志依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.33</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.33</version>
</dependency>三、Controller控制器
MVC请求处理流程
有了SpringMVC,不必再一个请求地址创建一个Servlet了,而是用DispatcherServlet代替Tomcat提供的默认的静态资源Servlet,它作为一个统一的访问点,所有的请求全部由他进行调度。
请求到达 Tomcat 服务器,由 Web 应用程序进行处理;DispatcherServlet 接收所有匹配其映射规则的请求;
HandlerMapping 根据请求 URL 找到对应的 Handler(Controller 方法)并包装成 HandlerExecutionChain(包含 Interceptors)。
依次执行 HandlerInterceptor 的 preHandle()。
HandlerAdapter 适配并调用 Handler(执行 Controller 方法)。Controller 处理请求,返回结果(可能是 ModelAndView、String 视图名、@ResponseBody 等)。
依次执行 HandlerInterceptor 的 postHandle()(如果 Controller 未抛出异常)。
如果返回的是视图,ViewResolver 将视图名解析为具体的 View 对象。View.render() 方法将模型数据渲染到响应(如生成 HTML)。
依次执行 HandlerInterceptor 的 afterCompletion()。
DispatcherServlet 将渲染结果返回给客户端。
- REST API 场景:如果使用
@RestController或@ResponseBody,流程会在Controller 处理请求后直接通过HttpMessageConverter序列化数据为 JSON/XML,跳过ViewResolver和View渲染。 - 静态资源:可通过
<mvc:resources>或ResourceHandlerRegistry配置,由 Spring 直接处理,不经过 Controller。
配置视图解析器和控制器
Thymeleaf
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>配置视图解析器:
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfiguration {
//我们需要使用ThymeleafViewResolver作为视图解析器,并解析我们的HTML页面
@Bean
public ThymeleafViewResolver thymeleafViewResolver(SpringTemplateEngine springTemplateEngine){
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setOrder(1); //可以存在多个视图解析器,并且可以为他们设定解析顺序
resolver.setCharacterEncoding("UTF-8"); //编码格式是重中之重
resolver.setTemplateEngine(springTemplateEngine); //和之前JavaWeb阶段一样,需要使用模板引擎进行解析,所以这里也需要设定一下模板引擎
return resolver;
}
//配置模板解析器
@Bean
public SpringResourceTemplateResolver templateResolver(){
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setSuffix(".html"); //需要解析的后缀名称
resolver.setPrefix("/"); //需要解析的HTML页面文件存放的位置,默认是webapp目录下,如果是类路径下需要添加classpath:前缀
return resolver;
}
//配置模板引擎Bean
@Bean
public SpringTemplateEngine springTemplateEngine(ITemplateResolver resolver){
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(resolver); //模板解析器,默认即可
return engine;
}
}控制器:
@Controller
public class HelloController {
@RequestMapping("/index")
public ModelAndView index() {
ModelAndView modelAndView = new ModelAndView("index");
modelAndView.getModel().put("name", "啊这"); //将name传递给Model
return modelAndView;
//返回后会经过视图解析器进行处理
}
}在类路径根目录下创建一个简单html文件:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="static/test.js"></script>
</head>
<body>
HelloWorld!
<div th:text="${name}"></div>
</body>
</html>静态资源访问
我们的页面中可能还会包含一些静态资源,比如js、css,因此这里我们还需要配置一下,让静态资源通过Tomcat提供的默认Servlet进行解析,我们需要让配置类实现一下WebMvcConfigurer接口,并重写以下方法:
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable(); //开启默认的Servlet
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
//配置静态资源的访问路径
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>测试</title>
<!-- 引用静态资源,这里使用Thymeleaf的网址链接表达式,Thymeleaf会自动添加web应用程序的名称到链接前面 -->
<script th:src="@{/static/test.js}"></script>
</head>
<body>
<p>欢迎来到网站</p>
</body>
</html>@RequestMapping
@RequestMapping({"/index", "/test"})
public ModelAndView index(){
return new ModelAndView("index");
}现在我们访问/index或是/test都会经过此方法进行处理。
添加路径前缀
将@RequestMapping添加到类名上,表示为此类中的所有请求映射添加一个路径前缀:
@Controller
@RequestMapping("/yyds")
public class MainController {
@RequestMapping({"/index", "/test"})
public ModelAndView index(){
return new ModelAndView("index");
}
}路径添加通配符
路径还支持使用通配符进行匹配:
- ?:表示任意一个字符,比如
@RequestMapping("/index/x?")可以匹配/index/xa、/index/xb等等。 - *:表示任意0-n个字符,比如
@RequestMapping("/index/*")可以匹配/index/lbwnb、/index/yyds等。 - **:表示当前目录或基于当前目录的多级目录,比如
@RequestMapping("/index/**")可以匹配/index、/index/xxx等。
Method属性
@RequestMapping(value = "/index", method = RequestMethod.POST)
public ModelAndView index(){
return new ModelAndView("index");
}使用衍生注解:
@PostMapping(value = "/index")
public ModelAndView index(){
return new ModelAndView("index");
}携带参数
比如这里我们要求请求中必须携带username和password属性,否则无法访问:
@RequestMapping(value = "/index", params = {"username", "password"})
public ModelAndView index(){
return new ModelAndView("index");
}它还支持表达式:
@RequestMapping(value = "/index", params = {"!username", "password"}):在username之前添加一个感叹号表示请求的不允许携带此参数,否则无法访问
@RequestMapping(value = "/index", params = {"username!=test", "password=123"}) :请求参数username不允许为test,并且password必须为123,否则无法访问。
请求头
@RequestMapping(value = "/index", headers = "!Connection"):如果请求头中携带了Connection属性,将无法访问。
其他两个属性:
- consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
- produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
@RequestParam和@RequestHeader
如何获取到请求中的参数?添加@RequestParam。
注:如果参数名称与形式参数名称相同,即使不添加@RequestParam也能获取到参数值。
一旦添加@RequestParam,那么此请求必须携带指定参数,我们也可以将require属性设定为false来将属性设定为非必须;
我们还可以直接设定一个默认值,当请求参数缺失时,可以直接使用默认值
@RequestMapping(value = "/index")
public ModelAndView index(@RequestParam(value = "username", required = false, defaultValue = "伞兵一号") String username){
System.out.println("接受到请求参数:"+username);
return new ModelAndView("index");
}如果需要使用Servlet原本的一些类,直接添加HttpServletRequest为形式参数即可,SpringMVC会自动传递该请求原本的HttpServletRequest对象;
@RequestMapping(value = "/index")
public ModelAndView index(HttpServletRequest request){
System.out.println("接受到请求参数:"+request.getParameterMap().keySet());
return new ModelAndView("index");
}同理,我们也可以添加HttpServletResponse作为形式参数,甚至可以直接将HttpSession也作为参数传递:
@RequestMapping(value = "/index")
public ModelAndView index(HttpSession session){
System.out.println(session.getAttribute("test"));
session.setAttribute("test", "鸡你太美");
return new ModelAndView("index");
}@RequestHeader与@RequestParam用法一致:
// 获取单个请求头
@GetMapping("/info")
public String getInfo(@RequestHeader("User-Agent") String userAgent) {
return "User Agent: " + userAgent;
}
// 使用默认值
@GetMapping("/lang")
public String language(@RequestHeader(value = "Accept-Language", defaultValue = "en") String lang) {
return "Language: " + lang;
}
// 获取所有请求头为Map
@GetMapping("/allHeaders")
public String allHeaders(@RequestHeader Map<String, String> headers) {
return "Headers: " + headers;
}
// 获取特定类型的请求头
@GetMapping("/content")
public String content(@RequestHeader("Content-Type") String contentType) {
return "Content Type: " + contentType;
}
// 获取多值请求头
@GetMapping("/accept")
public String accept(@RequestHeader("Accept") List<String> acceptHeaders) {
return "Accept headers: " + acceptHeaders;
}
// 获取HTTP日期头
@GetMapping("/ifModified")
public String ifModified(@RequestHeader("If-Modified-Since") Date ifModifiedSince) {
return "If-Modified-Since: " + ifModifiedSince;
}@CookieValue和@SessionAttrbutie
通过使用@CookieValue注解,我们也可以快速获取请求携带的Cookie信息;
@RequestMapping(value = "/index")
public ModelAndView index(HttpServletResponse response,
@CookieValue(value = "test", required = false) String test){
System.out.println("获取到cookie值为:"+test);
response.addCookie(new Cookie("test", "lbwnb"));
return new ModelAndView("index");
}同样的,Session也能使用注解@SessionAttribute快速获取:
@RequestMapping(value = "/index")
public ModelAndView index(@SessionAttribute(value = "test", required = false) String test,
HttpSession session){
session.setAttribute("test", "xxxx");
System.out.println(test);
return new ModelAndView("index");
}重定向和请求转发
| 特性 | 重定向(Redirect) | 请求转发(Forward) |
|---|---|---|
| 本质 | 两次独立的HTTP请求 | 一次HTTP请求,服务器内部转发 |
| 客户端感知 | 客户端知道URL变化(地址栏变化) | 客户端不知道(地址栏不变) |
重定向和请求转发,我们只需要在视图名称前面添加一个前缀即可。
通过添加redirect:前缀,就可以很方便地实现重定向:
@RequestMapping("/index")
public String index(){
return "redirect:home";
}
@RequestMapping("/home")
public String home(){
return "home";
}使用forward:前缀表示转发给其他请求映射:
@RequestMapping("/index")
public String index(){
return "forward:home";
}
@RequestMapping("/home")
public String home(){
return "home";
}Bean的Web作用域
在学习Spring时我们讲解了Bean的作用域,包括singleton和prototype,Bean分别会以单例和多例模式进行创建,而在SpringMVC中,它的作用域被继续细分:
- request:对于每次HTTP请求,使用request作用域定义的Bean都将产生一个新实例,请求结束后Bean也消失。
- session:对于每一个会话,使用session作用域定义的Bean都将产生一个新实例,会话过期后Bean也消失。
- global session:不常用
@Bean
@RequestScope
public TestBean testBean(){
return new TestBean();
}@Controller
public class MainController {
@Resource
TestBean bean;
@RequestMapping(value = "/index")
public ModelAndView index(){
System.out.println(bean);
return new ModelAndView("index");
}
}我们发现,每次发起请求得到的Bean实例都不同。
我们将其作用域修改为@SessionScope,这样作用域就上升到Session,只要清理浏览器的Cookie,那么都会被认为是同一个会话,只要是同一个会话,那么Bean实例始终不变。
实际上,它也是通过代理实现的,我们调用Bean中的方法会被转发到真正的Bean对象去执行。
RestFul风格&@PathVariable
中文释义为“表现层状态转换”。
RESTful风格的设计允许将参数通过URL拼接传到服务端,目的是让URL看起来更简洁实用;
并且我们可以充分使用多种HTTP请求方式(POST/GET/PUT/DELETE),来执行相同请求地址的不同类型操作。
我们可以按照不同功能进行划分:
- POST http://localhost:8080/mvc/index - 添加用户信息,携带表单数据
- GET http://localhost:8080/mvc/index/{id} - 获取用户信息,id直接放在请求路径中
- PUT http://localhost:8080/mvc/index - 修改用户信息,携带表单数据
- DELETE http://localhost:8080/mvc/index/{id} - 删除用户信息,id直接放在请求路径中
我们分别编写四个请求映射:
@Controller
public class MainController {
@RequestMapping(value = "/index/{id}", method = RequestMethod.GET)
public String get(@PathVariable("id") String text){
System.out.println("获取用户:"+text);
return "index";
}
@RequestMapping(value = "/index", method = RequestMethod.POST)
public String post(String username){
System.out.println("添加用户:"+username);
return "index";
}
@RequestMapping(value = "/index/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable("id") String text){
System.out.println("删除用户:"+text);
return "index";
}
@RequestMapping(value = "/index", method = RequestMethod.PUT)
public String put(String username){
System.out.println("修改用户:"+username);
return "index";
}
}四、Interceptor拦截器
过滤器是作用于Servlet之前,只有经过层层的过滤器才可以成功到达Servlet;
而拦截器是在Servlet与RequestMapping之间,相当于DispatcherServlet在将请求交给对应Controller中的方法之前进行拦截处理,它只会拦截所有Controller中定义的请求映射对应的请求(不会拦截静态资源)
创建拦截器
创建一个拦截器我们需要实现一个HandlerInterceptor接口:
public class MainInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("我是处理之前!");
return true; //只有返回true才会继续,否则直接结束
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("我是处理之后!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//在DispatcherServlet完全处理完请求后被调用
System.out.println("我是完成之后!");
}
}接着我们需要在配置类中进行注册:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MainInterceptor())
.addPathPatterns("/**") //添加拦截器的匹配路径,只要匹配一律拦截
.excludePathPatterns("/home"); //拦截器不进行拦截的路径
}如果处理过程中抛出异常,那么不会执行处理后postHandle方法,但是会执行afterCompletion方法,我们可以在此方法中获取到抛出的异常。
多级拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//一号拦截器
registry.addInterceptor(new MainInterceptor()).addPathPatterns("/**").excludePathPatterns("/home");
//二号拦截器
registry.addInterceptor(new SubInterceptor()).addPathPatterns("/**");
}与单个拦截器的情况一样,一旦拦截器返回false,那么之后无论有无拦截器,都不再继续。
五、异常处理
SpringMVC为我们提供了默认的异常处理页面,当我们的请求映射方法中出现异常时,会直接展示在前端页面,当出现异常时,我们的请求会被直接转交给专门用于异常处理的控制器进行处理。
我们可以自定义一个异常处理控制器,一旦出现指定异常,就会转接到此控制器执行:
@ControllerAdvice
public class ErrorController {
@ExceptionHandler(Exception.class)
public String error(Exception e, Model model){ //可以直接添加形参来获取异常
e.printStackTrace();
model.addAttribute("e", e);
return "500";
}
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
500 - 服务器出现了一个内部错误QAQ
<div th:text="${e}"></div>
</body>
</html>六、JSON数据格式与Axios请求
JSON数据格式
我们现在推崇的是前后端分离的开发模式,而不是所有的内容全部交给后端渲染再发送给浏览器,这就要用到Json。
JSON (JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。
JSON解析框架有很多种,比较常用的是Jackson和FastJSON。
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.34</version>
</dependency>let obj = JSON.parse('{"studentList": [{"name": "杰哥", "age": 18}, {"name": "阿伟", "age": 18}], "count": 2}')
//将JSON格式字符串转换为JS对象
obj.studentList[0].name //直接访问第一个学生的名称@RequestMapping(value = "/index")
public String index(){
JSONObject object = new JSONObject();
object.put("name", "杰哥");
object.put("age", 18);
JSONArray array = new JSONArray();
array.add(object);
System.out.println(array.toJSONString());
return "index";
}我们可以也直接创建一个实体类,将实体类转换为JSON格式的数据:
@RequestMapping(value = "/index", produces = "application/json")
@ResponseBody
public String data(){
Student student = new Student();
student.setName("杰哥");
student.setAge(18);
return JSON.toJSONString(student);
}SpringMVC非常智能,我们可以直接返回一个对象类型,它会被自动转换为JSON字符串格式,注意需要在配置类中添加一下FastJSON转换器,这里需要先添加一个依赖:
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension-spring6</artifactId>
<version>2.0.34</version>
</dependency>@RequestMapping(value = "/data", produces = "application/json")
@ResponseBody
public Student data(){
Student student = new Student();
student.setName("杰哥");
student.setAge(18);
return student;
}配置类实现WebMvcConfigurer接口,重写方法configureMessageConverters(),使用FastJsonHttpMessageConverter(),内容就会自动转换为JSON格式响应给客户端了。
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new FastJsonHttpMessageConverter());
}Axios异步请求
前端异步请求指的是在前端中发送请求至服务器或其他资源,并且不阻塞用户界面或其他操作。在传统的同步请求中,当发送请求时,浏览器会等待服务器响应,期间用户无法进行其他操作。而异步请求通过将请求发送到后台,在等待响应的同时,允许用户继续进行其他操作。这种机制能够提升用户体验,并且允许页面进行实时更新。
常见的前端异步请求方式包括使用XMLHttpRequest对象、Fetch API、以及使用jQuery库中的AJAX方法,以及目前最常用的Axios框架等。
Ajax(Asynchronous JavaScript and XML)技术:
Ajax工作流程
第一步:浏览器端发起请求
- 触发事件:浏览器中发生某个用户交互事件(如点击按钮、输入内容、页面滚动等)
- 创建请求对象:JavaScript创建XMLHttpRequest对象(现代浏览器也可使用Fetch API)
- 发送HTTP请求:通过XMLHttpRequest对象向服务器发送HTTP请求,此过程为异步操作,不会阻塞页面
第二步:服务器端处理
- 接收请求:服务器接收到浏览器发来的HTTP请求
- 处理请求:服务器根据请求内容执行相应的业务逻辑处理
- 返回数据:服务器创建响应,将处理结果以数据形式(通常是JSON或XML格式)返回给浏览器
第三步:浏览器端接收并处理
- 接收响应:浏览器接收到服务器返回的数据
- JavaScript处理:通过回调函数或Promise方式,使用JavaScript处理返回的数据
- 局部更新页面:根据处理结果,动态更新页面的部分内容(无需刷新整个页面)
Get
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
<script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>
</head>
<body>
<p>欢迎来到交友网站</p>
<p>用户名: <span id="username"></span></p>
<p>密码: <span id="password"></span></p>
</body>
</html><script>
function getInfo() {
axios.get('/mvc/test').then(({data}) => {
document.getElementById('username').innerText = data.username
document.getElementById('password').innerText = data.password
})
}
</script>Post
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
<script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>
</head>
<body>
<p>欢迎来到交友网站</p>
<button onclick="login()">立即登录</button>
</body>
</html>form表单形式:
<script>
function login() {
axios.post('/mvc/test', {
username: 'test',
password: '123456'
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(({data}) => {
if(data.success) {
alert('登录成功')
} else {
alert('登录失败')
}
})
}
</script>@ResponseBody
@PostMapping(value = "/test", produces = "application/json")
public String hello(String username, String password){
boolean success = "test".equals(user.getUsername()) && "123456".equals(user.getPassword());
JSONObject object = new JSONObject();
object.put("success", success);
return object.toString();
}json形式:
我们也可以将js对象转换为JSON字符串的形式进行传输,这里需要使用ajax方法来处理:
<script>
function login() {
axios.post('/mvc/test', {
username: 'test',
password: '123456'
}).then(({data}) => {
if(data.success) {
alert('登录成功')
} else {
alert('登录失败')
}
})
}
</script>如果我们需要读取前端发送给我们的JSON格式数据,那么这个时候就需要添加@RequestBody注解:
@ResponseBody
@PostMapping(value = "/test", produces = "application/json")
public String hello(@RequestBody User user){
boolean success = "test".equals(user.getUsername()) && "123456".equals(user.getPassword());
JSONObject object = new JSONObject();
object.put("success", success);
return object.toString();
}七、实现文件上传和下载
利用SpringMVC,我们可以很轻松地实现文件上传和下载,我们需要在MainInitializer中添加一个新的方法:
public class MainInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
...
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
// 直接通过registration配置Multipart相关配置,必须配置临时上传路径,建议选择方便打开的
// 同样可以设置其他属性:maxFileSize, maxRequestSize, fileSizeThreshold
registration.setMultipartConfig(new MultipartConfigElement("/Users/nagocoler/Download"));
}
}上传
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public String upload(@RequestParam MultipartFile file) throws IOException {
File fileObj = new File("test.png");
file.transferTo(fileObj);
System.out.println("用户上传的文件已保存到:"+fileObj.getAbsolutePath());
return "文件上传成功!";
}<div>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit">
</form>
</div>下载
直接使用HttpServletResponse,并向输出流中传输数据即可:
@GetMapping("/download")
public void download(@RequestParam("filename") String filename,
HttpServletResponse response) {
// 1. 验证文件名(防止路径遍历攻击)
if (!isValidFilename(filename)) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
return;
}
// 2. 构建文件路径
String filePath = "/path/to/files/" + filename;
File file = new File(filePath);
// 3. 检查文件是否存在
if (!file.exists()) {
response.setStatus(HttpStatus.NOT_FOUND.value());
return;
}
// 4. 设置响应头
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + encodeFilename(filename) + "\"");
response.setContentLength((int) file.length());
// 5. 写入文件流
try (InputStream is = new FileInputStream(file);
OutputStream os = response.getOutputStream()) {
IOUtils.copy(is, os);
} catch (IOException e) {
throw new RuntimeException("文件下载失败", e);
}
}
// 中文文件名编码处理
private String encodeFilename(String filename) {
try {
return URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
return filename;
}
}
// 防止路径遍历攻击
private boolean isValidFilename(String filename) {
// 检查是否包含路径分隔符
return filename != null && !filename.contains("..") && !filename.contains("/");
}<!-- 传递文件名参数 -->
<a href="/download?filename=test.png" download="test.png">下载test.png</a>
<a href="/download?filename=report.pdf" download="report.pdf">下载报告</a>
<a href="/download?filename=数据报表.xlsx" download="数据报表.xlsx">下载Excel</a> 本文系作者 @xiin 原创发布在To Future$站点。未经许可,禁止转载。
暂无评论数据