👏 Hi! 我是 Yumuing,一个技术的敲钟人
👨💻 每天分享技术文章,永远做技术的朝拜者
📚 欢迎关注我的博客:Yumuing's blog
注:本文是以前初学时期的笔记上传,如有错误,还望指出,一定改正,谢谢支持!有兴趣也可以关注我的博客:yumuing 博客
Controller 相关介绍
Controller 在 spring 中代表的是控制层,是将访问者请求进行分发调用不同函数,来控制获取请求参数以及返回业务层处理完的数据给访问者的层面。它在 spring 中必须在 Controller 类前添加 @Controller 注解,以注入容器中,并发挥作用。
在 Controller 中存在两种传递参数的方式,第一种是采用路径后添加 ? 参数的方式,第二种则是采用参数直接写入路径的方式,如下:
第一种:yumuing/params?username=yumuing&password=1234
第二种:yumuing/user/{userId}
在 Get 请求中,获取第一种参数的方式,主要是利用 @RequestParam 这个注解得到同名的参数,获取第二种参数的方式则是采用 @PathVariable 这个注解获取 @RequestMapping 中的大括号标记的同名参数,其中,@RequestParam 和 @PathVariable 都是作为方法参数书写的,必须注明参数类型(如 String ),@RequestMapping 则是方法整体注解,在添加新参数时,路径得使用 path 进行指定,而不是一个简单的字符串书写即可,来如下:
//第一种
@RequestMappingpath="/params",method=RequestMethod.GETpath = {
"/params"},method = {
RequestMethod.GET}path="/params",method=RequestMethod.GET
public void params@RequestParam(name="username",required=false,defaultValue="test"@RequestParam(name = "username", required = false,defaultValue = "test"@RequestParam(name="username",required=false,defaultValue="test"String username);
//第二种
@RequestMappingpath="/user/userId",method=RequestMethod.GETpath = "/user/{userId}",method = {
RequestMethod.GET}path="/user/userId",method=RequestMethod.GET
public void params@PathVariable("userId"@PathVariable("userId"@PathVariable("userId" int userId);
当然,@RequestMapping 的参数还有很多,但必须注意的一点是尽可能使用 大括号(即 {},会更加直观,也防止部分一定要求需要大括号的参数出错,并且这个注解既可以一个函数,也可以注解一个类。当注解一个类时,类中所有的方法都会在这个基础上再进行过滤,参数类型基本包括如下:
- path:路径参数,书写格式参照 url 后缀格式的字符串(即 ”/yumuing/user“ )参数指定了访问该方法的具体路径,不指定参数名时,默认为填写内容为 path 的参数内容,path也可以是一个未定的变量。
- method:访问方法参数,主要包括 Get(RequestMethod.GET)、Post(RequestMethod.POST)、Delete(RequestMethod.DELETE)、Put(RequestMethod.PUT)等等,都是调用的方式进行填写的。
- params:请求参数,不可与 RequestParam 中的参数重复定义,会发生冲突,没有优先级之分。书写格式为字符串,如 ”yumuing“
根据以上三个参数,我们就能够让一个 controller 通过请求 url、参数、访问方法来进行区分并处理对应请求,并分别调用不同的函数,返回所需结果。
如果,想要在浏览器中进行简单的打印验证,可以考虑在对应 Controller 函数前添加 @RequestBody,并指定返回参数为 String ,它将会只打印返回的 String 数据,而不去进行渲染等等操作。如果,不使用 @RequestBody 即默认返回 HTML 数据。
当然,@RequestBody 也可以作为函数参数在将方法指定为只能使用 Post 请求之后,只接受请求体内容。与其不同的 @RequestParam,则是接收并解析请求体中以 key-value 格式的数据。两者可以同时使用,也可单独使用。
//打印示例
@RequestMapping("/hello")
@ResponseBody
public String hello() {
return "hello";
}
返回的结果基本分为两种,一是数据,二是模板,数据可以是一个简单的 int 类型,也可以是 json 类型,乃至其他类型,而模板即是传递给浏览器渲染的 jsp、html 等等格式的内容。
在 thymeleaf 中,模板文件默认放在 resources/templates 中,可以存在子目录。模板文件基本配置如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- xmlns:th="http://www.thymeleaf.org"设置读取该模板文件 -->
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<!--具体内容 -->
<!-- 示例内容 -->
<p th:text="${name}"></p>
<p th:text="${age}"></p>
<!-- th:text="${age}"匹配对应内容 -->
</body>
</html>
最终响应到浏览器的模板数据如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>hhhh</p>
<p>30</p>
</body>
</html>
Controller 访问模板文件示例:
@RequestMapping(path = "/test",method = RequestMethod.GET)
public ModelAndView test(){
ModelAndView mav = new ModelAndView();
mav.addObject("name","hhh");
mav.addObject("age",30);
mav.setViewName("/demo/view");
return mav;
}
@RequestMapping(path = "/test02",method = RequestMethod.GET)
public String test02(Model model){
model.addAttribute("name","hhhh");
model.addAttribute("age",30);
return "/demo/view";
}
第一种是将 model 和 view 数据都装一个对象里,最后把两者一起返回,而第二种是将 model 放到参数里,把模板文件返回。从代码阅读来看,第二种更为简洁直观,更推荐使用。注意第一种使用的是 addObject(),第二种用的是 addAttrubute(),两种不可混用,并且注意第一种返回数据为 ModelAndView ,第二种返回数据为 String,当然,如果函数参数没有携带 org.springframework.ui.Model 的对象,将不会被识别为返回模板文件,而是返回字符串。
接下来,我们就介绍 json 数据是该怎么样去通过 Controller 传输的。它基本是发生在一次异步请求中的,也就是说,每次请求数据,返回模板数据,而是返回 json 数据,以浏览器更新已有的模板文件,不会导致每次请求的数据量传输过大,影响用户体验。
thymeleaf 模板引擎配合下,Controller 只添加 @RequestMapping 注解默认传输模板文件,当添加 @ResponseBody 后,我们就能传输 json 数据,当然,返回数据类型不能是 String 类型,否则,就只会打印,而不会转成 json 格式数据,并打印出来了。如下:
@RequestMapping(path = "/emp",method = RequestMethod.GET)
@ResponseBody
public Map<String,Object> getEmp(){
Map<String,Object> emp = new HashMap<>();
emp.put("name","张三");
emp.put("age",16);
emp.put("code",778522);
return emp;
}
访问 localhost:8888/yumuing/emp ,返回数据并打印在浏览器数据如下:
{
"code":778522,"name":"张三","age":16}
bug 总结
请求参数定义多次,无法访问
出现原因:
- 在 @RequestMapping 中已经定义 params,又在 @RequestParam 中定义 params,导致出现错误。
- 如果参数前写了@RequestParam(xxx),并且没有添加 require = false ,那么前端必须有对应的xxx名字才行,不然,就会发生错误。
- @RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。否则也会发生错误。
- 访问静态文件时,路径为放置在 resource/static 目录下的相对路径,如 test.html 文件在/resourse/static/html/test.html,路径即为 localhost:端口号/html/test.html,除非配置了全局url 前缀。
错误效果如下:
访问页面显示
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
控制台输出
2023-02-03T21:33:35.418+08:00 INFO 24684 --- [nio-8888-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-02-03T21:33:35.418+08:00 INFO 24684 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-02-03T21:33:35.418+08:00 INFO 24684 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
2023-02-03T21:33:35.430+08:00 WARN 24684 --- [nio-8888-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.UnsatisfiedServletRequestParameterException: Parameter conditions "username, password" not met for actual request parameters: ]
解决方案:
- 第一种:删去 @RequestMapping 中多余的定义即可。
- 第二种:要么添加 require = false ,要么请求添加对应参数,要么后端不加该参数
- 第三种:使用 Post 请求,或者删去这个注解
- 第四种:书写正确路径即可
注:System.out.printf() 为控制台输出,return、HttpServletResponse等等都是页面输出。
找不到模板文件
控制台报错:
Error resolving template template might not exist or might not be accessible;
解决方案:
修改 application.yml 文件中的配置为:
保证能够读取 html 文件,注意前有一英文半角 ”点“ ,设置 cache 为不缓存,保证实时修改,实时生效,生产环境推荐,方便调试,部署环境改为缓存型。设置文件路径,/templates 后不要加斜杆。
spring: thymeleaf: suffix: .html encoding: utf-8 mode: HTML5 cache: false prefix: classpath:/template
修改 Controller 函数访问模板路径为为相对路径,如:/demo/view ,记得最前面有斜杠,最后面没有斜杠,不需要写后缀名,默认 html
本文测试代码均在我的博客 Spring Controller 基本认识及应用获取