SpringMVC入门
SpringMVC概述
三层架构
浏览器发送一个请求给后端服务器,后端服务器现在是使用Servlet来接收请求和数据 如果所有的处理都交给Servlet来处理的话,所有的东西都耦合在一起,对后期的维护和扩展极为 不利 将后端服务器Servlet拆分成三层,分别是web、service和dao web层主要由servlet来处理,负责页面请求和数据的收集以及响应结果给前端 service层主要负责业务逻辑的处理 dao层主要负责数据的增删改查操作
servlet处理请求和数据的时候,存在的问题是一个servlet只能处理一个请求 针对web层进行了优化,采用了MVC设计模式,将其设计为controller、view和Model controller负责请求和数据的接收,接收后将其转发给service进行业务处理 service根据需要会调用dao对数据进行增删改查 dao把数据处理完后将结果交给service,service再交给controller controller根据需求组装成Model和View,Model和View组合起来生成页面转发给前端浏览 器 这样做的好处就是controller可以处理多个请求,并对请求进行分发,执行不同的业务操 作。
因为是异步调用,所以后端不需要返回view视图,将其去除 前端如果通过异步调用的方式进行交互,后台就需要将返回的数据转换成json格式进行返回
SpringMVC 主要 负责的就是
controller如何接收请求和数据
如何将请求和数据转发给业务层
如何将响应数据转换成json发回到前端
介绍了这么多,对SpringMVC进行一个定义
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架 优点 使用简单、开发便捷(相比于Servlet)
灵活性强
SpringMVC入门案例
SpringMVC具体的实现流程
1.创建web工程(Maven结构)
2.设置tomcat服务器,加载web工程(tomcat插件)
3.导入坐标( SpringMVC +Servlet)
4.定义处理请求的功能类( UserController )
5. 设置请求映射(配置映射关系)
6. 将SpringMVC设定加载到Tomcat容器中
步骤1:创建Maven项目
步骤2:补全目录结构
步骤3:导入jar包 <?xml version="1.0" encoding="UTF-8"?> 4.0.0 com.itheima springmvc_01_quickstart 1.0-SNAPSHOT war javax.servlet javax.servlet-api 3.1.0 provided org.springframework spring-webmvc 5.2.10.RELEASE org.apache.tomcat.maven tomcat7-maven-plugin 2.1 8888 /
说明:
servlet的坐标为什么需要添加provided ? scope是maven中jar包依赖作用范围的描述, 如果不设置默认是compile在在编译、运行、测试时均有效 如果运行有效的话就会和tomcat中的servlet-api包发生冲突,导致启动报错
provided代表的是该包只在编译和测试的时候用,运行的时候无效直接使用tomcat中的,就避免冲突.
步骤4:创建配置类 package com.itheima.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; //springmvc配置类,本质上还是一个spring配置类 @Configuration @ComponentScan("com.itheima.controller") public class SpringMvcConfig { }
步骤5:创建Controller类
步骤6:使用配置类替换web.xml
将web.xml删除,换成ServletContainersInitConfig package com.itheima.config; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer; //web容器配置类 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //加载springmvc配置类,产生springmvc容器(本质还是spring容器) protected WebApplicationContext createServletApplicationContext() { //初始化WebApplicationContext对象 AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); //加载指定配置类 ctx.register(SpringMvcConfig.class); return ctx; } //设置由springmvc控制器处理的请求映射路径 protected String[] getServletMappings() { return new String[]{"/"}; } //加载spring配置类 protected WebApplicationContext createRootApplicationContext() { return null; } }
步骤7:配置Tomcat环境
IDEA配置Tomcat环境
步骤8:启动运行项目
步骤9:设置返回数据为json
如果方法直接返回字符串,springmvc会把字符串当成页面的名称在项目中进行查找 返回,因为不存在对应返回值名称的页面,所以会报404错误,找不到资源。 而我们其实是想要直接返回的是json数据,具体如何修改呢?
添加注解@ResponseBodypackage com.itheima.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; //定义表现层控制器bean @Controller public class UserController { //设置映射路径为/save,即外部访问路径 @RequestMapping("/save") //设置当前操作返回结果为指定json数据(本质上是一个字符串信息) @ResponseBody public String save(){ System.out.println("user save ..."); return "{"info":"springmvc"}"; } }
工作流程解析
为了更好的使用SpringMVC,我们将SpringMVC的使用过程总共分两个阶段来分析,分别是启动服务 器初始化过程和单次请求过程
启动服务器初始化过程
1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器 功能类似于以前的web.xml
2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象 该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器
3. 加载SpringMvcConfig配置类
4. 执行@ComponentScan加载对应的bean
扫描指定包及其子包下所有类上的注解,如Controller类上的@Controller注解
5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
此时就建立了 /save 和 save方法的对应关系
6. 执行getServletMappings方法,设定SpringMVC拦截请求的路径规则
/代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求
单次请求过程
1. 发送请求http://localhost/save
2. web容器发现该请求满足SpringMVC拦截规则,将请求交给SpringMVC处理
3. 解析请求路径/save
4. 由/save匹配执行对应的方法save() 上面的第五步已经将请求路径和方法建立了对应关系,通过/save就能找到对应的save方法
5. 执行save()
6. 检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方
bean加载控制 config目录存入的是配置类,写过的配置类有: ServletContainersInitConfig SpringConfig SpringMvcConfig JdbcConfig MybatisConfig controller目录存放的是SpringMVC的controller类 service目录存放的是service接口和实现类 dao目录存放的是dao/Mapper接口
controller、service和dao这些类都需要被容器管理成bean对象,那么到底是该让SpringMVC加 载还是让Spring加载呢? SpringMVC加载其相关bean(表现层bean),也就是controller包下的类 Spring控制的bean
业务bean(Service)
功能bean(DataSource,SqlSessionFactoryBean,MapperScannerConfigurer等)
分析清楚谁该管哪些bean以后,接下来要解决的问题是如何让Spring和SpringMVC分开加载各自的 内容。
在SpringMVC的配置类SpringMvcConfig中使用注解@ComponentScan,我们只需要将其扫描范围设 置到controller即可,如
在Spring的配置类SpringConfig中使用注解@ComponentScan ,当时扫描的范围中其实是已经包含 了controller,如:
从包结构来看的话,Spring已经多把SpringMVC的controller类也给扫描到,所以针对这个问题 该如何解决,就是咱们接下来要学习的内容。
概括的描述下咱们现在的问题就是 因为功能不同,如何避免Spring错误加载到SpringMVC的bean?
针对上面的问题,解决方案也比较简单,就是:
加载Spring控制的bean的时候排除掉SpringMVC控制的bean
具体该如何排除:
方式一:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
方式二:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包中的bean
方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中[了解即可]
设置bean加载控制
方式一:修改Spring配置类,设定扫描范围为精准范围。 package com.itheima.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; @Configuration @ComponentScan({"com.itheima.service","com.itheima.dao"}) public class SpringConfig { }
说明:
上述只是通过例子说明可以精确指定让Spring扫描对应的包结构,真正在做开发的时候,因为Dao最 终是交给MapperScannerConfigurer对象来进行扫描处理的,我们只需要将其扫描到service包可。
方式二:修改Spring配置类,设定扫描范围为com.itheima,排除掉controller包中的bean package com.itheima.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; @Configuration //设置spring配置类加载bean时的过滤规则,当前要求排除掉表现层对应的bean //excludeFilters属性:设置扫描加载bean时,排除的过滤规则 //type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除 //classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean @ComponentScan(value="com.itheima", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { } excludeFilters属性:设置扫描加载bean时,排除的过滤规则 type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除
ANNOTATION:按照注解排除
ASSIGNABLE_TYPE:按照指定的类型过滤
ASPECTJ:按照Aspectj表达式排除,基本上不会用
REGEX:按照正则表达式排除
CUSTOM:按照自定义规则排除
大家只需要知道第一种ANNOTATION即可 classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean
请求与响应
设置映射路径 package com.itheima.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class BookController { //请求路径映射 @RequestMapping("/book/save") @ResponseBody public String save(){ System.out.println("book save ..."); return "{"module":"book save"}"; } } package com.itheima.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller //类上方配置的请求映射与方法上面配置的请求映射连接在一起,形成完整的请求映射路径 @RequestMapping("/user") public class UserController { //请求路径映射 @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{"module":"user save"}"; } //请求路径映射 @RequestMapping("/delete") @ResponseBody public String delete(){ System.out.println("user delete ..."); return "{"module":"user delete"}"; } }
请求参数
请求路径设置好后,只要确保页面发送请求地址和后台Controller类中配置的路径一致,就可以接 收到前端的请求,接收到请求后,如何接收页面传递的参数?
关于请求参数的传递与接收是和请求方式有关系的,目前比较常见的两种请求方式为: GET POST
GET发送单个参数
发送请求与参数:
http://localhost/commonParam?name=itcast
接收参数: //普通参数:请求参数与形参名称对应即可完成参数传递 @RequestMapping("/commonParam") @ResponseBody public String commonParam(String name ){ System.out.println("普通参数传递 name ==> "+name); return "{"module":"common param"}"; }
GET发送多个参数
发送请求与参数:
http://localhost/commonParam ?name=itcast&age=15
接收参数: //普通参数:请求参数与形参名称对应即可完成参数传递 @RequestMapping("/commonParam") @ResponseBody public String commonParam(String name ,int age){ System.out.println("普通参数传递 name ==> "+name); System.out.println("普通参数传递 age ==> "+age); return "{"module":"common param"}"; }
GET请求中文乱码
出现乱码的原因相信大家都清楚,Tomcat8.5以后的版本已经处理了中文乱码的问题,但是IDEA中的Tomcat插件目前只到Tomcat7,所以需要修改pom.xml来解决GET请求中文乱码问题
UTF-8 org.apache.tomcat.maven tomcat7-maven-plugin 2.1 8999 / UTF-8
POST发送参数
发送请求与参数:
接收参数:
和GET一致,不用做任何修改 //普通参数:请求参数与形参名称对应即可完成参数传递 @RequestMapping("/commonParam") @ResponseBody public String commonParam(String name ,int age){ System.out.println("普通参数传递 name ==> "+name); System.out.println("普通参数传递 age ==> "+age); return "{"module":"common param"}"; }
POST请求中文乱码
解决方案:配置过滤器 package com.itheima.config; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import javax.servlet.Filter; public class servletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[0]; } protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } //乱码处理 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8"); return new Filter[]{filter}; } }
CharacterEncodingFilter是在spring-web包中,所以用之前需要导入对应的jar包。
五种类型参数传递 普通参数 POJO类型参数 嵌套POJO类型参数 数组类型参数 集合类型参数
普通参数 普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。
如果形参与地址参数名不一致该如何解决?
解决方案:使用@RequestParam注解 //普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系 @RequestMapping("/commonParamDifferentName") @ResponseBody public String commonParamDifferentName(@RequestParam("name") String userName , int age){ System.out.println("普通参数传递 userName ==> "+userName); System.out.println("普通参数传递 age ==> "+age); return "{"module":"common param different name"}"; }
注意:写上@RequestParam注解框架就不需要自己去解析注入,能提升框架处理性能
POJO数据类型
简单数据类型一般处理的是参数个数比较少的请求,如果参数比较多,那么后台接收参数的时候就比 较复杂,这个时候我们可以考虑使用POJO数据类型。 POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数
此时需要使用前面准备好的POJO类,先来看下User package com.itheima.domain; public class User { private String name; private int age; private Address address; @Override public String toString() { return "User{" + "name="" + name + """ + ", age=" + age + ", address=" + address + "}"; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
发送请求和参数:
后台接收参数: //POJO参数:请求参数与形参对象中的属性对应即可完成参数传递 @RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("pojo参数传递 user ==> "+user); return "{"module":"pojo param"}"; }
注意: POJO参数接收,前端GET和POST发送请求数据的方式不变。 请求参数key的名称要和POJO中属性的名称一致,否则无法封装。 嵌套POJO类型参数
如果POJO对象中嵌套了其他的POJO类,如 public class Address { private String province; private String city; //setter...getter...略 } public class User { private String name; private int age; private Address address; //setter...getter...略 } 嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属 性参数
发送请求和参数:
后台接收参数 //嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递 @RequestMapping("/pojoContainPojoParam") @ResponseBody public String pojoContainPojoParam(User user){ System.out.println("pojo嵌套pojo参数传递 user ==> "+user); return "{"module":"pojo contain pojo param"}"; }
请求参数key的名称要和POJO中属性的名称一致,否则无法封装
数组类型参数
举个简单的例子,如果前端需要获取用户的爱好,爱好绝大多数情况下都是多个,如何发送请求数据 和接收数据呢? 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数 发送请求和参数:
//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中 @RequestMapping("/arrayParam") @ResponseBody public String arrayParam(String[] likes){ System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes)); return "{"module":"array param"}"; }
集合类型参数
数组能接收多个值,那么集合是否也可以实现这个功能呢?
发送请求和参数
:
后台接收参数: //集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据 @RequestMapping("/listParam") @ResponseBody public String listParam(@RequestParam List likes){ System.out.println("集合参数传递 likes ==> "+ likes); return "{"module":"list param"}"; }
SpringMVC将List看做是一个POJO对象来处理,将其创建一个对象并准备把前端的数 据封装到对象中.
JSON数据传输参数
前面我们说过,现在比较流行的开发方式为异步调用。前后台以异步方式进行交换,传输的数据使用 的是 JSON ,所以前端如果发送的是JSON数据,后端该如何接收?
对于JSON数据类型,我们常见的有三种: json普通数组(["value1","value2","value3",...]) json对象({key1:value1,key2:value2,...}) json对象数组([{key1:value1,...},{key2:value2,...}])
对于上述数据,前端如何发送,后端如何接收?
JSON普通数组
步骤1:pom.xml添加依赖
SpringMVC默认使用的是jackson来处理json的转换,所以需要在pom.xml添加jackson依赖 com.fasterxml.jackson.core jackson-databind 2.9.0
步骤2:PostMan发送JSON数据
步骤3:开启SpringMVC注解支持
在SpringMVC的配置类中开启SpringMVC的注解支持,这里面就包含了将JSON转换成对象的功能。 package com.itheima.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import javax.servlet.Filter; import javax.servlet.annotation.WebFilter; @Configuration @ComponentScan("com.itheima.controller") //开启json数据类型自动转换 @EnableWebMvc public class SpringMvcConfig { }
步骤4:参数前添加@RequestBody //集合参数:json格式 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据 @RequestMapping("/listParamForJson") @ResponseBody public String listParamForJson(@RequestBody List likes){ System.out.println("list common(json)参数传递 list ==> "+likes); return "{"module":"list common for json param"}"; }
步骤5:启动运行程序
JSON普通数组的数据就已经传递完成,下面针对JSON对象数据和JSON对象数组的数据该如何传递呢?
JSON对象数据
我们会发现,只需要关注请求和数据如何发送?后端数据如何接收?
请求和数据的发送:
{
"name" : "itcast" ,
"age" : 15
}
后端接收数据: //POJO参数:json格式 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc //2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应 @RequestMapping("/pojoParamForJson") @ResponseBody public String pojoParamForJson(@RequestBody User user){ System.out.println("pojo(json)参数传递 user ==> "+user); return "{"module":"pojo for json param"}"; }
启动程序访问测试
说明:
address为null的原因是前端没有传递数据给后端。
如果想要address也有数据,我们需求修改前端传递的数据内容:
{
"name" : "itcast" ,
"age" : 15 ,
"address" :
{
"province" : "beijing" ,
"city" : "beijing"
}
}
JSON对象数组
集合中保存多个POJO该如何实现?
请求和数据的发送:
[
{ "name" : "itcast" , "age" : 15 },
{ "name" : "itheima" , "age" : 12 }
]
后端接收数据: //集合参数:json格式 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应 @RequestMapping("/listPojoParamForJson") @ResponseBody public String listPojoParamForJson(@RequestBody List list){ System.out.println("list pojo(json)参数传递 list ==> "+list); return "{"module":"list pojo for json param"}"; }
小结
SpringMVC接收JSON数据的实现步骤为:
(1)导入jackson包
(2)使用PostMan发送JSON数据
(3)开启SpringMVC注解驱动,在配置类上添加@EnableWebMvc注解
(4)Controller方法的参数前添加@RequestBody注解
@RequestBody与@RequestParam区别
区别
@RequestParam用于接收url地址传参,表单传参【application/x-www-formurlencoded】
@RequestBody用于接收json数据【application/json】
@RequestMapping ( "/listPojoParamForJson" )
@ResponseBody
public String listPojoParamForJson ( @RequestBody List < User > list ){
System . out . println ( "list pojo(json)参数传递 list ==> " + list );
return "{"module":"list pojo for json param"}" ;
}
应用 后期开发中,发送json格式数据为主,@RequestBody应用较广 如果发送非json格式数据,选用@RequestParam接收请求参数
日期类型参数传递
前面我们处理过简单数据类型、POJO数据类型、数组和集合数据类型以及JSON数据类型,接下来我们 还得处理一种开发中比较常见的一种数据类型,日期类型
日期类型比较特殊,因为对于日期的格式有N多中输入方式,比如: 2088-08-18 2088/08/18 08/18/2088 ......
针对这么多日期格式
,SpringMVC该如何接收,它能很好的处理日期类型数据么?
步骤1:编写方法接收日期数据
在UserController类中添加方法,把参数设置为日期类型 //日期参数 //使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd @RequestMapping("/dataParam") @ResponseBody public String dataParam(Date date, @DateTimeFormat(pattern="yyyy-MM-dd") Date date1, @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){ System.out.println("参数传递 date ==> "+date); System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1); System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2); return "{"module":"data param"}"; }
SpringMVC默认支持的字符串转日期的格式为yyyy/MM/dd,如果想使用其他格式,需要使用@DateTimeFormat。
响应
对于响应,主要就包含两部分内容: 响应页面 响应数据 文本数据 json数据 package com.itheima.controller; import com.itheima.domain.User; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList; import java.util.Date; import java.util.List; @Controller public class UserController { //响应页面/跳转页面 //返回值为String类型,设置返回值为页面名称,即可实现页面跳转 @RequestMapping("/toJumpPage") public String toJumpPage(){ System.out.println("跳转页面"); return "page.jsp"; } //响应文本数据 //返回值为String类型,设置返回值为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解 @RequestMapping("/toText") @ResponseBody public String toText(){ System.out.println("返回纯文本数据"); return "response text"; } //响应POJO对象 //返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解 @RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO(){ System.out.println("返回json对象数据"); User user = new User(); user.setName("itcast"); user.setAge(15); return user; } //响应POJO集合对象 //返回值为集合对象,设置返回值为集合类型,即可实现返回对应集合的json数组数据,需要依赖@ResponseBody注解和@EnableWebMvc注解 @RequestMapping("/toJsonList") @ResponseBody public List toJsonList(){ System.out.println("返回json集合数据"); User user1 = new User(); user1.setName("传智播客"); user1.setAge(15); User user2 = new User(); user2.setName("黑马程序员"); user2.setAge(12); List userList = new ArrayList(); userList.add(user1); userList.add(user2); return userList; } }
Rest风格
REST简介
REST (Representational State Transfer),表现形式状态转换,它是一种软件架构 风格
当我们想表示一个网络资源的时候,可以使用两种方式:
传统风格资源描述形式
http://localhost/user/getById?id=1 查询id为1的用户信息
http://localhost/user/saveUser 保存用户信息
REST风格描述形式
http://localhost/user/1 http://localhost/user
传统方式一般是一个请求url对应一种操作,这样做不仅麻烦,也不安全,因为会程序的人读取了你的 请求url地址,就大概知道该url实现的是一个什么样的操作。 查看REST风格的描述,你会发现请求地址变的简单了,并且光看请求URL并不是很能猜出来该URL的 具体功能 所以REST的优点有:
隐藏资源的访问行为,无法通过地址得知对资源是何种操作 书写简化 但是我们的问题也随之而来了,一个相同的url地址即可以是新增也可以是修改或者查询,那么到底我 们该如何区分该请求到底是什么操作呢?
按照REST风格访问资源时使用 行为动作 区分对资源进行了何种操作 http://localhost/users 查询全部用户信息 GET(查询) http://localhost/users/1 查询指定用户信息 GET(查询) http://localhost/users 添加用户信息 POST(新增/保存) http://localhost/users 修改用户信息 PUT(修改/更新) http://localhost/users/1 删除用户信息 DELETE(删除)
请求的方式比较多,但是比较常用的就4种,分别是GET , POST , PUT , DELETE。
按照不同的请求方式代表不同的操作类型。
发送GET请求是用来做查询
发送POST请求是用来做新增发送
PUT请求是用来做修改
发送DELETE请求是用来做删除
清楚了什么是REST风格后,我们后期会经常提到一个概念叫RESTful,那什么又是RESTful呢?
根据REST风格对资源进行访问称为 RESTful 。 后期我们在进行开发的过程中,大多是都是遵从REST风格来访问我们的后台服务,所以可以说咱们以 后都是基于RESTful来进行开发的。 package com.itheima.controller; import com.itheima.domain.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller public class UserController { //设置当前请求方法为POST,表示REST风格中的添加操作 @RequestMapping(value = "/users",method = RequestMethod.POST) @ResponseBody public String save(){ System.out.println("user save..."); return "{"module":"user save"}"; } //设置当前请求方法为DELETE,表示REST风格中的删除操作 //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同 @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id){ System.out.println("user delete..." + id); return "{"module":"user delete"}"; } //设置当前请求方法为PUT,表示REST风格中的修改操作 @RequestMapping(value = "/users",method = RequestMethod.PUT) @ResponseBody public String update(@RequestBody User user){ System.out.println("user update..."+user); return "{"module":"user update"}"; } //设置当前请求方法为GET,表示REST风格中的查询操作 //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同 @RequestMapping(value = "/users/{id}" ,method = RequestMethod.GET) @ResponseBody public String getById(@PathVariable Integer id){ System.out.println("user getById..."+id); return "{"module":"user getById"}"; } //设置当前请求方法为GET,表示REST风格中的查询操作 @RequestMapping(value = "/users",method = RequestMethod.GET) @ResponseBody public String getAll(){ System.out.println("user getAll..."); return "{"module":"user getAll"}"; } } /* @RequestMapping @ResponseBody public String delete(){ System.out.println("user delete..."); return "{"module":"user delete"}"; } @RequestMapping @ResponseBody public String update(){ System.out.println("user update..."); return "{"module":"user update"}"; } @RequestMapping @ResponseBody public String getById(){ System.out.println("user getById..."); return "{"module":"user getById"}"; } @RequestMapping @ResponseBody public String getAll(){ System.out.println("user getAll..."); return "{"module":"user getAll"}"; } */package com.itheima.controller; import com.itheima.domain.Book; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; //@Controller //@ResponseBody配置在类上可以简化配置,表示设置当前每个方法的返回值都作为响应体 //@ResponseBody @RestController //使用@RestController注解替换@Controller与@ResponseBody注解,简化书写 @RequestMapping("/books") public class BookController { // @RequestMapping( method = RequestMethod.POST) @PostMapping //使用@PostMapping简化Post请求方法对应的映射配置 public String save(@RequestBody Book book){ System.out.println("book save..." + book); return "{"module":"book save"}"; } // @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE) @DeleteMapping("/{id}") //使用@DeleteMapping简化DELETE请求方法对应的映射配置 public String delete(@PathVariable Integer id){ System.out.println("book delete..." + id); return "{"module":"book delete"}"; } // @RequestMapping(method = RequestMethod.PUT) @PutMapping //使用@PutMapping简化Put请求方法对应的映射配置 public String update(@RequestBody Book book){ System.out.println("book update..."+book); return "{"module":"book update"}"; } // @RequestMapping(value = "/{id}" ,method = RequestMethod.GET) @GetMapping("/{id}") //使用@GetMapping简化GET请求方法对应的映射配置 public String getById(@PathVariable Integer id){ System.out.println("book getById..."+id); return "{"module":"book getById"}"; } // @RequestMapping(method = RequestMethod.GET) @GetMapping //使用@GetMapping简化GET请求方法对应的映射配置 public String getAll(){ System.out.println("book getAll..."); return "{"module":"book getAll"}"; } }
新增 //设置当前请求方法为POST,表示REST风格中的添加操作 @RequestMapping(value = "/users",method = RequestMethod.POST) @ResponseBody public String save(){ System.out.println("user save..."); return "{"module":"user save"}"; }// @RequestMapping( method = RequestMethod.POST) @PostMapping //使用@PostMapping简化Post请求方法对应的映射配置 public String save(@RequestBody Book book){ System.out.println("book save..." + book); return "{"module":"book save"}"; }
删除
前端发送请求的时候使用: http://localhost/users/1 ,路径中的1就是我们想要传递的参数。
后端获取参数,需要做如下修改: 修改@RequestMapping的value属性,将其中修改为/users/{id},目的是和路径匹配 在方法的形参前添加@PathVariable注解 //设置当前请求方法为DELETE,表示REST风格中的删除操作 //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同 @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id){ System.out.println("user delete..." + id); return "{"module":"user delete"}"; }// @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE) @DeleteMapping("/{id}") //使用@DeleteMapping简化DELETE请求方法对应的映射配置 public String delete(@PathVariable Integer id){ System.out.println("book delete..." + id); return "{"module":"book delete"}"; }
如果有多个参数需要传递该如何编写?
前端发送请求的时候使用: http://localhost/users/1/tom ,路径中的1和tom就是我们想要传递的 两个参数。 后端获取参数,需要做如下修改: @Controller public class UserController { //设置当前请求方法为DELETE,表示REST风格中的删除操作 @RequestMapping(value = "/users/{id}/{name}",method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id,@PathVariable String name) { System.out.println("user delete..." + id+","+name); return "{"module":"user delete"}"; } }
修改 //设置当前请求方法为PUT,表示REST风格中的修改操作 @RequestMapping(value = "/users",method = RequestMethod.PUT) @ResponseBody public String update(@RequestBody User user){ System.out.println("user update..."+user); return "{"module":"user update"}"; } 将请求路径更改为/users
访问该方法使用 PUT: http://localhost/users 访问并携带参数:
根据ID查询 //设置当前请求方法为GET,表示REST风格中的查询操作 //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同 @RequestMapping(value = "/users/{id}" ,method = RequestMethod.GET) @ResponseBody public String getById(@PathVariable Integer id){ System.out.println("user getById..."+id); return "{"module":"user getById"}"; }
查询所有 //设置当前请求方法为GET,表示REST风格中的查询操作 @RequestMapping(value = "/users",method = RequestMethod.GET) @ResponseBody public String getAll(){ System.out.println("user getAll..."); return "{"module":"user getAll"}"; }
小结
RESTful入门案例,我们需要学习的内容如下:
(1)设定Http请求动作(动词)
@RequestMapping(value="", method = RequestMethod. POST|GET|PUT|DELETE )
(2)设定请求参数(路径变量)
@RequestMapping(value="/users/ {id} ",method = RequestMethod.DELETE)
@ReponseBody public String delete( @PathVariable Integer id ){ }
关于接收参数,我们学过三个注解@RequestBody、@RequestParam、@PathVariable ,这三个注解 之间的区别和应用分别是什么?
区别
@RequestParam用于接收url地址传参或表单传参
@RequestBody用于接收json数据
@PathVariable用于接收路径参数,使用{参数名称}描述路径参数
应用
后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
如果发送非json格式数据,选用@RequestParam接收请求参数
采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路 径变量,通常用于传递id值.
SpringMVC需要将静态资源进行放行。 package com.itheima.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; @Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { //设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载 @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { //当访问/pages/????时候,从/pages目录下查找内容 registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
该配置类是在config目录下,SpringMVC扫描的是controller包,所以该配置类还未生效,要 想生效需要将SpringMvcConfig配置类进行修改 @Configuration @ComponentScan({"com.itheima.controller","com.itheima.config"}) @EnableWebMvc public class SpringMvcConfig { } 或者 @Configuration @ComponentScan("com.itheima") @EnableWebMvc public class SpringMvcConfig { }
如果在贵州茅台上市当年就买入500股,持有到现在市值有多大?如果在2001年买入贵州茅台500股,一直持有到现在就是20年的时间,在这20年的时间里市值到底会增长多少?能带来的投资收益有多少呢?有人说可以买一套房,也有人说是几千倍收益。贵州
当年73胜勇士为什么还要让杜兰特加入?勇士73胜靠的是球队完美的团队体系以及库里那个赛季完全无法防守的神准三分球,库里神奇的巅峰赛季场均30。1分5。4篮板6。7助攻加上180俱乐部的命中率,在历史上几乎无人能及。但是
为什么在老上海市民中很流行盐城方言俨然是上海的第二普通话?盐城,因汉武帝时期遍地皆为煮盐场,到处都是运盐河而设县为盐渎,至东晋七年(公元411年)改名盐城,使用至今。从地理上划分,盐城隶属于江苏,地处长江北,俗称江北,也叫苏北。老上海市民
中国女乒,陈梦,孙颖莎,王曼昱,谁是真正的领军人物?陈梦孙颖莎和王曼昱都应该看好。因为她们在巴黎奥运会之前,都已成长为为主国女乒的顶梁柱为领军人物,是女乒在各项国际大赛中争金夺银的好手。三人久经磨砺,各具特色,在竞争中互有胜负也很正
沪漂一族,来上海打拼你们后悔了吗?天下没有后悔药买,后悔不后悔是自己的事,为了孩子只有拼博,上海是国际大都市,教育,医疗,治安都比其它地方好,是文明大上海,为了自己不要沪漂,沪漂过得很坚苦,没有意义了!早点回家,你
温格对阿森纳贡献大吗?如果大,为什么当年都希望他下课?贡献大。但不是历史上贡献大就一辈子衣食无忧。这是一个工作,当总是不赢球,球迷可以看你资格老给你一个赛季两个赛季,但始终达不到要求,打不进前四的时候就还是得下课。更衣室离心离德,控制
在上海工作的东北人多吗?有居住证,加社保的不多,头条上有这方面官方统计数据,东北人在上海排名全国各地后十位。但官方统计数据来自于办理过居住证,缴纳五险一金的非常驻人口。而哪些没有办理过的归类流动人口。如果
谁能讲一个真实的故事?平均年龄40岁儿女靠70岁母亲微薄工资生活,却每天大鱼大肉,一天儿子看见母亲从陌生男子房间出来。随后就去超市买鱼买肉,大包小包的拎回家。儿子十分愤怒,甚至嗤之以鼻。他打心底里瞧不起
自闭症孩子会有哪些异常表现?哪些行为需要引起家长关注?自闭症也称孤独症,此病一般好发于宝宝3岁以内,以社会交往障碍交流障碍局限的兴趣刻板与重复行为方式为主要临床表现,多数患儿还会伴有不同程度的精神发育迟滞。婴幼儿时期的宝宝自闭症早期表
在手术室里的患者们是怎么度过手术时光的?2019年的年底,我经历了两次手术,一次小手术一次大手术,一次局麻和一次全麻!第一次是活检小手术(取颈部淋巴结)。人处于完全清醒的状态,我脱掉上衣,反穿手术服,自己走进手术室躺在手
为什么很多有钱人的子女往往很优秀?1有钱人告诉子女的都是有用的干货,让子女少走弯路。2有钱人的子女从小上的都是好学校。3有钱的家庭氛围更好,更民主和自由。4有钱人家的孩子,从小吃过见过,不会被小恩小惠所迷惑。5有钱
Meta演示QuestProNBA观赛片段可在MR和VR中自然过渡近期,MetaCEO马克扎克伯格公布了一段在QuestPro中观看NBA比赛的demo视频,视频中展示了一种仿佛在NBA球场边看比赛的VR体验。据悉,NBA已经与Meta达成合作,
周末去哪早春漫游津沽,去这些景点邂逅不一样的天津头条创作挑战赛提到天津,您的脑海中可能会浮现关于海河瓷房子五大道的美好回忆。除了这些经典去处,本期文旅君为您带来几处别具特色的景点,一起探索另一种风格的天津。天津市滨海新区图书馆天
黔西红色村庄奏响绿色发展新曲老板,做5个人的鱼火锅。好,快进来坐,今天的鱼新鲜得很!3月19日中午时分,黔西市大关镇丘林村村民张德平家里宾客满座,前来村里旅游的人大多到他们家品尝鱼火锅。青瓦白墙的小楼前,乌江
庄河草莓节启幕3月18日,大连乡村振兴旅游季2023中国大连(庄河)国际草莓节在庄河万达广场启幕,庄河市十余家景区现场推介景区特色和优惠政策。此次草莓节以旅游农业为主题,进一步宣传庄河,提升城市
典型案例提供酒店在线预订服务方应当履行协助退订等合同附随义务熊某等诉某旅行社网络服务合同纠纷案基本案情原告熊某通过某旅游APP向被告某旅行社预订了机票酒店自由行产品。出行前两日,因同行人员中原告儿子患病无法出行,原告遂向被告申请退订。被告就该酒店产品联系其中间供应商,中间供
十里杏花开满山,平安旅途警相随(通讯员付朝霞)花开赞皇大地春天沿着山麓升起赞皇十里杏花沟期待与您一期一会赞皇交警会一直陪伴在您身边为每一位游客旅途平安保驾护航又是一年芳草绿,依然十里杏花香。赞皇县首届杏花节开幕
花漾姑苏,美到我心巴上了春天到啦总要在苏州街头看一次花吧樱花灿若云霞,桃花枝头春俏梨花翩翩倩影,蔷薇花开烂漫满城花开限定浪漫待君来赏玉兰玉兰花晶莹皎洁,灵动娇俏,盛放在蓝天丽日之下,让人不禁驻足凝望,享受
济南这些打卡地,最近又火了来源爱济南新闻客户端进入3月济南这些地方的花开了成了市民和游客的打卡地卧牛山3月18日,在济南卧牛山地质公园内,大片的二月兰在林间竞相绽放,一朵朵紫色的小花形成紫色花海,就像给大地
酷炫的光影体验,就在崇明这里你知道吗?在东平国家森林公园内藏着一个沉浸式光影主题馆,展馆以森林为主题分为一个序厅和五个展厅,通过艺术化的设计手法,将森林有关的文化知识营造出神秘有趣的氛围,唯美炫酷,一起去看看
法国遭遇干旱蒙贝尔湖湖面缩减超80央视新闻客户端继去年夏天的严重干旱之后,欧洲多国目前再次遭遇旱情。近日,在法国西南部,由于降雨稀少,当地的一个湖泊面积大幅缩减,当地的农业生产和旅游业发展面临严重威胁。蒙贝尔湖是法
呈贡区第十一届万溪梨花节开幕!邀你赴一场春日盛宴走进万溪冲社区,梨花争芳斗艳昆明信息港讯记者段佳琪忽如一夜春风来,千树万树梨花开。3月18日,2023年呈贡区网络文化节系列活动暨中国呈贡第十一届万溪梨花节开幕,走进万溪冲社区,百