《Spring Boot 实战派》--02 Spring Boot 应用
5. 分层开发web应用程序

如果读者对MVC 开发模式理解得不深入,那么往往会以为用户通过浏览器访问 MVC 模型的页面就是访问视图 ( View ) 。实际上, 它并不是直接访问视图,
而是访问 DispatcherServlet处理映射和调用视图渲染,然后返回给用户的数据;
- value :指定请求的地址
- method :指定请求的 method 类型-GET 、HEAD、 POST、 PUT、 PATCH、 DELETE、 OPTIONS、 TRACE
- consumes :消费消息,指定处理请求的提交内容类型( Content-Type ),例如 application/json、text/html
- produces :生产消息,指定返回的内容类型。 仅当request 请求头中的 Accept 类型中包含该指定类型时才返回
- params :指定request 中必须包含某些参数值才让该方法处理请求。
- headers :指定request 中必须包含某些指定的 header 值才能让该方法处理请求
1 2 3 4 5 6 7 | @RequestMapping (value = "/product/{id}" method = RequestMethod.GET) public String getProduct( @PathVaribale ( "id" ) String id){ Product product = productRepository.findById(id); System.out.println( "产品id:" + product.getld()); System.out.printin( "产品名称:" +product.getTitle()); return "product/show" ; } |

1 2 3 4 5 6 7 | @GetMapping (”/{id}”) public ModelAndView getArticle( @PathVariable ( "id" ) Integer id) throws Exception { Article ticles = articleRepository.findById(id); ModelAndView mav = new ModelAndView("article/show”); mav.addObject( "article" , articles); return mav; } |
2. DELETE
- 使用 try catch exception :如果不发生异常,则默认为成功,但是这样并不好。
- 通过存储过程的返回值来判断是否正确执行:如果执行成功,则返回 1或大于0 的值;如果执行失败,则返回0
- 在执行 DELETE 方法前先查询是否有数据:在执行 DELETE 方法后返回值是 0,所以,一般先查询 一下是否有数据。
1 2 3 4 5 6 7 8 | •GET在浏览器中可以回退,而POST 访问同一个地址时也是再次提交请求 •GET请求会被浏览器主动缓存,而 POST 则不会 •GET中的参数会被完整地保留在浏览器历史记录里,而 POST 中的参数则不会t1呆留 •GET只能进行 URL 编码,而 POST 支持多种编码方式 •GET只接收 ASCII字符,而 POST 没有限制 •GET的安全性相比POST 低,因为参数直接暴露在 URL上,所以不能用它传遂敏感信息 •GET 的参数是通过URL 传递的,而 POST 的参数是放在request body 中的 但是,以上这些都不是绝对的,比如 POST 也可以通过 URL 路径提交参数 |
4. PUT
5.1处理内容类型
1. 认识Http中的媒体类型content-Type
1 2 3 4 5 | @RestController @RequestMapping (value = "/{id}”, method=RequestMethod.GET,produces=" application/json") public Model getModel ( @PathVariable String id, Model model) { // } |
如果要强制返回编码,则加上编码类型,如以下代码:
1 | produces = "MediaType.APPLICATION_JSON_VALUE"+" ;charset=utf- 8 ") |
1 2 3 4 5 | @RestController @RequestMapping (value = "/{id}”, method=RequestMethod.GET,consumes=" application/json") public void getModel ( @RequestBody Model model) { // } |
5.2 在方法中使用参数
1. 获取路径中的值
1 2 3 4 5 6 7 8 9 10 11 | /** * Description: 根据 id 获取文章对象 * */ @GetMapping ( "article/{id}" ) public ModelAndView getArticle( @PathVariable ( "id" ) Integer id) { Article articles = articleRepositor.findByld(id); ModelAndView mav = new ModelA idView( "article/show" ) mav.addObject("article’’, articles); reture mav; } |
2. 获取路径中的参数
对于路径中的参数获取, 可以写入方法的形参中,下面代码是获取参数 username 的值;
1 2 3 | @RequestMapping ( "/addUser" ) public String addUser(String username){ } |
这里的参数和上面所讲的获取路径值是不一样的, 比如:http://localhost/user/?username=longzhiran, 它是由"="隔开的
3. 通过Bean接收HTTP提价的对象
可以通过Bean 获取HTTP提交的对象, 如以下代码:
1 2 | public String addUser(UserModel user){ } |
4. 通过HttpServletRequest接收的参数
可以通过HttpServletRequest接收参数, 如下代码:
1 2 3 4 5 | @RequestMapping ( "/addUser" ) public String addUser(HttpServletRequest request){ Syso( "name:" +request.getParameter( "username" )) return "/index" ; } |
6. 用@RequestParam绑定入参
1 | @RequestParam (value= "username" , required= false ) |
当请求参数不存在时会有异常发生, 可以通过设置属性“required=false”来解决
7. 用@RequestBody接收JSON数据
可以通过@RequestBody 注解来接收JSON数据, 如下代码:
1 2 3 4 5 | @RequestMapping ( "/addUser" , method={RequestMethod.POST}) @ResponseBody public void saveUser( @RequestBody List<User> users){ userService.saveUser(users); } |
8. 上传文件 MultipartFile
通过@RequestParam 获取文件, 如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public String singleFileUpload( @RequestParam ( "file" )MultipartFile file, RedirectAttributes redirectAttributes) { if (file.isEmpty()) { edirectAttributes.addFlashAttribute( "message" ,“请选择文件"); return "redirect:uploadStatus" ; } try { byte [] bytes= file.getBytes(); Path path= Paths.get(UPLOADED_FOLDER + file.getOriginalFilename()); Files.write(path, bytes); edirectAttributes.addFlashAttribute( "message" , "成功上传" + file.getOriginalFilename() + "" ); } catch (IOException e) { e.printStackTrace(); } return "redirect:/uploadStatus" ; } |
处于安全考虑, 在生产环境中需要判断文件的类型, 一般不允许上传“.exe”等格式的可执行文件
9. 上传图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | long i = System.currentTimeMillis(); //新建日期对象 Data date = new Date(); //转换日期输出格式 SimpleDateFormat format = new SimpleDataFormat( "yyyyMMdd" ); String nyr = format.format(date); private static String UPLOADED_FOLDER = "/UPLOAD/img/”; @PostMapping ( "/upload" ) @ResponseBody //注意, ckeditor 上传的是 upload 字段 public Map<String, Object> singleFileUpload( @RequestParam (”upload”) MultipartFile file, RedirectAttributes redirectAttributes) { Map<St ing, Object> map= new HashMap<St ing, Object>(); if (file.getOriginalFilename().endsWith{”.jpg”) || file.getOriginalFilename().endsWith(”.png”) || file.getOriginalFilename().endsWith(”.gif")) { try { byteD bytes = file.getBytes(); String s = nyr + Math.random() + file.getOriginalFilename(); Path path = Paths.get(UPLOADED_FOLDER + s); Files.write(path, bytes); map.put(”uploaded”, 1 ); map.put(”fileName”, s); map.put(”url”,”/UPLOAD/img/”+ s); map.put(”message”,”上传成功”); reture map; //return "({”uploaded": "","fileName":"fileName","message":"上传成功" }]; } catch (IOException e) { e.p intStackTrace(); } else { map.put(”uploaded”, O); map.put(”fileName”, file.getOriginalFilename()); map.put( "url" , "/img/" +file.getOriginalFilename()); map.put( "message" ,”图片后缀只支持 png,jpg,gif,请检查!"); return map; } return map;} |
5.3 验证数据
1、认识内置的验证器Hibernate-validator
Hibernate-validator 可实现数据的验证,它是对 JSR (Java Specification Requests )标准 的实现。在Web开发中,不需要额外为验证再导入其他依赖,只需要添加Web依赖即可。
Web 依赖不只集成了 Hibernate-validator,还有如下的子依赖:
- spring-boot-starter
- spring-boot-starter-json。
- spring-boot-starter-tomcat
- hibernate-validator
- spring-web
- spring-webmvc
由此可见,Web依赖集成了服务器环境(Tomcat)、JSON、MVC、Validator。在开发Web 时,只需要关注业务逻辑即可
Spring Boot 2.1.3的Validator的版本是6.0.16,是依据JSR-380标准实现的,其常用注解见表
表 Validator验证的常用注解:
注 解 |
作用类型 |
说 明 |
@NotBlank(message =) |
字符串 |
验证字符串非null,且长度必须大于0 |
|
字符串 |
被注释的元素必须是电子邮箱地址 |
@Length(min=,max=) |
字符串 |
被注释的字符串的大小必须在指定的范围内,min代表最小, max代表最大 |
@NotEmpty |
字符串 |
被注释的字符串必须非空 |
@NotEmptyPattem |
字符串 |
在字符串不为空的情况下,是否匹配正则表达式 |
@DateValidator |
字符串 |
验证日期格式是否满足正则表达式,Local为英语 |
@DateFormatCheckPattern |
字符串 |
验证日期格式是否满足正则表达式,Local是自己手动指定的 |
@CreditCardNumber |
字符串 |
验证信用卡号码 |
@Range(min=,max=,message=) |
数值类型、字 符串、字节等 |
被注释的元素必须在合适的范围内 |
@Null |
任意 |
被注释的元素必须为null |
@NotNull |
任意 |
被注释的元素必须不为null |
@AssertTrue |
布尔偵 |
被注释的元素必须为true |
@AssertFalse |
布尔值 |
被注释的元素必须为false |
@Min(value) |
数字 |
被注释的元素必须是一个数字,且大于或等于指定的最小值 |
@Max(value) |
数字 |
被注释的元素必须是一个数字,且小于或等于指定的最大值 |
@DecimalMin(value) |
数字 |
被注释的元素必须是一个数字,且大于或等于指定的最小值 |
@DecimalMax(value) |
数字 |
被注释的元素必须是一个数字,且小于或等于指定的最大值 |
@Size(max=,min=) |
数字 |
被注释的元素的大小必须在指定的范围内,min代表最小, max代表最大 |
@Digits (integer, fraction) |
数字 |
被注释的元素必须是一个数字,且在可接收的范围内 |
@Past |
日期 |
被注释的元素必须是一个过去的日期 |
注 解 |
作用类型 |
说 明 |
©Future |
日期 |
被注释的元素必须是一个将来的日期 |
@Pattern(regex=,flag= |
正则表达式 |
被注释的元素必须符合指定的正则表达式 |
@ListStringPattem |
List<String> |
验证集合中的字符串是否满足正则表达式 |
2、自定义验证功能
自定义验证需要提供两个类:①自定义注解类;②自定义验证业务逻辑类。
1. 自定义注解类
如果要自定义验证功能,则需要先自定义注解,以便在实体Bean中使用它,见以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package com.example.demo; import com.example.MyCustomConstraintValidator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //限定使用范围一一只能在字段上使用 @Target ((ElementType.FIELD)) //表明注解的生命周期,它在代码运行时可以通过反射获取到注解 @Retention (RetentionPolicy.RUNTIME) //©Constraint注解,里面传入了一个validatedBy字段,以指定该注解的校验逻辑 @Constraint (validatedBy = MyCustomConstraintValidator. class ) public © interface MyCustomConstraint { /** * ©Description:错误提示 */ String message() default "请输入中国政治或经济中心的城市名" ; Class<?>[] groups default {}; Class<? extends Payload>[] payload() default {}; } |
2. 自定义验证业务逻辑类
在自定义验证实现类中需要两个方法(initialize和isValid ) ----始化验证消息的方法和执行验证的方法。
在初始化验证消息的方法中,可以得到配置的注解内容;而验证方法则是用来验证业务逻辑的, 它需要继承 ConstraintValidator接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.example; import com.example.demo.MyCustomConstraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class MyCustomConstraintValidator implements ConstraintValidator<MyCustomConstraint, String> { //String为校验的类型 @Override public void initialize(MyCustomConstraint myConstraint) ( //在启动时执行 } /** * @Description:自定义校验逻辑 */ @Override public boolean isValid(String s, ConstraintValidatorContext validatorContext) ( if (!(s.equals(”北京”) || s.equals( "上海" ))){ return false ; } return true ; } } |
注解@MyCustomConstraint已被成功定义,可以在其他类中调用它来实现验证功能。
3、验证表单数据并实现数据的自定义验证
a.创建实体
创建实体Bean,用于表单验证。这里要特别注意的是,定义的所有字段都需要被验证,否则会出错;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package com.example.demo.entity; import com.example.demo.MyCustomConstraint; import lombok.Data; import org.hibernate.validator.constraints. Length; import javax.validation.constraints.*; import java.io.Serializable; @Data public class User implements Serializable ( /** * 主键id */ private Long id; @NotBlank (message = ”用户名不能为空”) @Length (min = 5 , max= 20 , message= "用户名长度为 5-20 个字符" ) private String name; @NotNull (message = "年龄不能为空" ) @Min (value = 18 , message = "最小18岁" ) @Max (value = 60 , message ="最大 60 岁”) private Integer age; @Email (message = "请输入邮箱" ) @NotBlank (message = ”邮箱不能为空”) private String email; @MyCustomConstraint private String answer; } |
b.编写验证控制器
编写控制器,用于绑定数据验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package com.example.demo.controller; import com.example.demo.entity.User; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.servlet.mvc.support.RedirectAttributes; <br> import javax.validation.Valid;<br> ©Controller public class TestValidator (<br> @GetMapping ( "/test" ) public String showForm(User user) ( return "form" ; }<br> @GetMapping ( "/results" ) public String results() ( return "results" ; }<br> @PostMapping ( "/test" ) public String checkUser( @Valid User user, BindingResult bindingResult, RedirectAttributes attr) { //特别注意:实体中的属性都必须被验证过,否则不会成功 if (bindingResult.hasErrors()) ( return "form" ; ) attr.addFlashAttribute( "user" , user); return "redirect:/results" ; } ) |
代码解释如下。
- addAttribute 方法:用 RedirectAttributes 的 addAttribute 方法传递参数,参数应跟在 URL 后面。
- addFlashAttribute方法:参数不会跟在URL后面,会被暂存在session中。
redirect方法:必须是方法的映射路径。redirect后的值只会岀现一次,刷新后不会出现。 可以使用redirect方法来防止重复提交。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现