SpringBoot笔记3——控制器知识点总结


声明:本文转载自https://my.oschina.net/happyBKs/blog/1622412,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

想了想有必要把控制器的一些内容放在系列里一起写一下,虽然之前springMVC的博文中已经介绍了很多有关Controller的东西,但是现在既然希望大家全面使用springboot,所以把这部分内容也放在本系列中总结概括一下吧。

控制器:接收和处理客户端的请求,spring通过注解控制器类,加载特定的控制器。

控制器相关注解

注解有三种:

@Controller 处理http请求
@RestController spring4之后新加的注解,原来返回json需要@ResponseBody配合@Controller
@RequestMapping 配置url映射(从请求url(可能还包括请求方法、参数(pathvariable或parameter)等到控制器及对应方法的映射))

@RestController和@Controller

上一篇文章中我们直接使用的是@RestController,从表面上看,控制器各个方法返回的String值都被作为页面内容返回给客户端,实际操作可以等价于@Controller和@Response共同注解的结果。

如果单用@Controller注解该类,我们会发现访问url将不能返回正常结果:

例如我们再新建一个新的控制器类,用@Controller注解。

package com.happybks.pets;  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod;  @Controller public class MyController {      @Autowired     PetProperties petProperties;      @RequestMapping(value = "/handle",method = RequestMethod.GET)     public String handle(){         return "handlepage";//petProperties.toString();     } }

然后启动访问http://localhost:8689/happybks/handle

但是提示404。

真实的原因是没有提供配置模板。响应给用户的可视化的页面,一般通过一定的模板页面给用户。无论是原装的JSP,还是后来的velcity、freemarker等模板都是传统的后端程序员必备的技术之一。但是在当今互联网,或者说如今的技术大背景下,这些模板技术的风光不再,因为大方向是前后端分离,后端开发者只需要提供restful风格的接口给前端开发者,由前端开发者来负责数据的展示与交互。性能原因是,模板技术一般性能比较吃力,耗费资源。

但是,作为一个纯粹的、脱离了前端低级趣味的后端开发人:),有必要了解至少一种模板技术以备不时之需,也能给自己的技术池拼接完整。

今天我想介绍spring自己的一个模板技术thymeleaf。关于这个模板技术与别的有啥改进和优点,请参考:

http://www.infoq.com/cn/news/2011/08/thymeleaf-1.0.0-template-engine

我们首先需要引入thymeleaf的jar,在pom文件中加入依赖坐标:

		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-thymeleaf</artifactId> 			<version>1.5.10.RELEASE</version> 		</dependency>

(本文出自oschina博主happyBKs的博文:https://my.oschina.net/happyBKs/blog/1622412)

然后在项目的resources目录下新建一个templates目录存放存放加入模板。(在mvn项目中,resources目录不仅存放配置文件,还存放模板)idea在新建springboot init项目时已经为我们把templates文件夹都建好了。

handlepage.html的内容如下:

<!DOCTYPE html> <html> <head>     <meta charset="UTF-8" />     <title>handle</title> </head> <body> <h1>HappyBKs at OSC</h1> <h1>thymeleaf</h1> <h1>spring boot start</h1> </body> </html>

注意,刚才控制器类的handle方法的返回值是String,值为handlepage,即模板的名称。

我们运行后,得到结果。

 

如果我们在方法上添加一个注解@ResponseBody,那么返回的字符串会作为相应内容返回。如果注解在方法上,对方法的返回值有效,方法的返回值将作为相应内容返回给客户端。如果@ResponseBody注解在类上,对该控制器类的所有方法有效,所有方法的返回值作为模板名称,由spring分发转给对应的模板页面并返回给客户端。

package com.happybks.pets;  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody;  @Controller public class MyController {      @Autowired     PetProperties petProperties;      @RequestMapping(value = "/handle",method = RequestMethod.GET)     public String handle(){         return "handlepage";//petProperties.toString();     }      @RequestMapping(value = "/handle2",method = RequestMethod.GET)     @ResponseBody     public String handle2(){         return "handlepage";//petProperties.toString();     } } 

访问:http://localhost:8689/happybks/handle2

注解在控制器类上也可:

package com.happybks.pets;  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody;  @Controller @ResponseBody public class MyController {      @Autowired     PetProperties petProperties;      @RequestMapping(value = "/handle2",method = RequestMethod.GET)     public String handle2(){         return "handlepage";//petProperties.toString();     } } 

 

@RequestMapping的知识点

@RequestMapping的知识点有以下几个:

1、控制器类 及 其方法都可以注解@RequestMapping,访问请求的URL为控制器类与下属方法的注解值路径的拼接路径

2、@RequestMapping注解可路径、方法、参数等。

public @interface RequestMapping {     java.lang.String name() default "";      @org.springframework.core.annotation.AliasFor("path")     java.lang.String[] value() default {};      @org.springframework.core.annotation.AliasFor("value")     java.lang.String[] path() default {};      org.springframework.web.bind.annotation.RequestMethod[] method() default {};      java.lang.String[] params() default {};      java.lang.String[] headers() default {};      java.lang.String[] consumes() default {};      java.lang.String[] produces() default {}; }

3、@RequestMapping注解支持映射多个路径为其值。

从上面的@RequestMapping注解的定义也可以看出,value或path对应的是一个String数组,这也说明了@RequestMapping注解可以将一个控制器类的一个方法映射到多个路径上。

    @RequestMapping(value = {"/handle3","/petProperties"},method = RequestMethod.GET)     @ResponseBody     public String handle3(){         return petProperties.toString();     }

运行请求http://localhost:8689/happybks/handle3和http://localhost:8689/happybks/petProperties

结果都是一样的:

 

Post请求响应

处理一般键值对表单参数

处理一般键值对表单参数,实际接收的是一个k1=v1&k2=v2的String类型的参数。

新添加一个控制器,在@RequestMapping注解的method的属性指定RequestMethod类型为POST

    @RequestMapping(value = "/handle4",method = RequestMethod.POST)     @ResponseBody     public String handle4(@RequestBody String formkvs){         String[] kvs = formkvs.split("&");         final HashMap<String, String> formkvsMap=new HashMap<>();         for(String itemKV:kvs){             String[] fields = itemKV.split("=");             final String key = fields[0];             final String value = fields[1];             formkvsMap.put(key, value);         }         return formkvsMap.get("username") + "buy a " + formkvsMap.get("product");     }

如果此时运行,我们直接浏览器访问http://localhost:8689/happybks/handle4,因为是GET请求,所以请求方法错误报错405。

我们使用postman来实验

注意,post请求参数是在requestbody中。一般的键值对表单参数,默认使用x-www-form-urlencoded。

在spring中,控制器处理表单参数使用的@RequestBody,注解到对应的方法形参上,String类型的形参对应于表单中的各个键通过k1=v1&k2=v2的拼接形式传递。

在postman中,我们填写两个参数,然后我们发出请求,可以看到response返回的结果

 

关于表单类型,附加一个说明如下:

form元素有个enctype属性,可以指定数据编码方式,有如下三种:

1. application/x-www-form-urlencoded: 表单数据编码为键值对,&分隔

2. multipart/form-data: 表单数据编码为一条消息,每个控件对应消息的一部分

3. text/plain: 表单数据以纯文本形式进行编码

详细说明:

form的enctype的编码方式,常用有两种:

application/x-www-form-urlencoded和multipart/form-data

其中 application/x-www-form-urlencoded为默认编码方式。

在form的action为get时,浏览器用x-www-form-urlencoded的编码方式,将表单数据编码为(name1=value1&name2=value2...),然后把这个字符串append到url后面,用?分隔,跳转到这个新的url

当form的action为post时,浏览器将form数据封装到http body中,然后发送到server。

在没有type=file时候,用默认的 application/x-www-form-urlencoded 就行。

在有 Content-type=file 时候,要用multipart/form-data编码方式。浏览器会把表单以控件为单位分割,并且为每个部分加上Content-Dispositon(form-data或file)、Content-Type(默认text/plain)、name(控件name)等信息,并加上分割符(boundary)。

postman的参考文章:http://blog.csdn.net/wangjun5159/article/details/47781443

 

处理json参数的请求

    @RequestMapping(value = "/handle5",method = RequestMethod.POST)     @ResponseBody     public String handle5(@RequestBody TradeItem tradeItem){         return tradeItem.getUsername() + " buy a " + tradeItem.getProduct();     }

然后运行,用postman请求

http://localhost:8689/happybks/handle5

这时候我们的配置方式要注意一下

一般键值对表单可以使用request的默认content-ype类型,但是json参数的请求必须使用application/json。

具体方法是,在Body中选择raw(原始body文本编辑方式),然后右侧会多出一个下拉选择框,选择JSON(application/json),然后在下方直接编辑一个json字符串。注意http request中的json需要用双引号,别跟Python代码的dict弄混了。

如果你从Body标签切换到Header标签,可以看到:

点击postman的Code,可以看到你浏览器调试http请求的代码。

运行send:

 

 

其他注意知识点:

如果@RequestMapping没有指定method属性的值,那么无论get还是post类型的请求都会被该控制器方法响应处理。但是不推荐这种不指定的做法。

 

参数注解

前面我们使用@RequestBody注解处理request的请求参数。

实际上@RequestBody针对的是整个request的body数据,不仅仅限制于请求参数。

@RequestBody注解String方法形参,形参的值为k1=v1&k2=v2字符串,content-ype类型为默认application/x-www-form-urlencoded;

@RequestBody注解bean类型形参,则处理的是content-ype类型为application/json的json为body内容的请求。

处理请求参数,其实还有更为方便的注解——@RequestParameter,具体我们放在后面再细说。

有关请求参数处理有以下几个注解:

@PathVariable 获取截断出url路径中的数据
@RequestParam 获取请求参数的值
@GetMapping 组合注解

 

@PathVariable注解

    @RequestMapping(value = "/handle6/{username}/{articleId}",method = RequestMethod.GET)     @ResponseBody     public String handle6(@PathVariable String username, @PathVariable("articleId") String aid){         return username + " write an article, id is " + aid;     }

住的注意的是,形参的名称可以与url请求中的路径中的表达式{}参数名称不同,但是需要在形参注解上标注对应的url路径参数名称;不标注的话,默认按照形参名称寻找url中对应的{}参数值。

请求http://localhost:8689/happybks/handle6/happybks/1234

 

@RequestParam

@RequestParam是一个专门面向参数处理的注解,而之前提到的@RequestBody是处理请求body的注解。@RequestParam会直接把请求中的参数封装好取出,无论是get请求url参数还是post请求的表单参数都支持@RequestParam注解获取。

get方式示例:

    @RequestMapping(value = "/handle7",method = RequestMethod.GET)     @ResponseBody     public String handle7(@RequestParam String username, @RequestParam String product){         return username+ " buy a " + product;     }

运行请求http://localhost:8689/happybks/handle7?username=happybks&product=house

 

post请求方式:

    @RequestMapping(value = "/handle7",method = RequestMethod.POST)     @ResponseBody     public String handle7(@RequestParam String username, @RequestParam String product){         return username+ " buy a " + product;     }

运行后,请求http://localhost:8689/happybks/handle7,post表单参数设置如下:

响应结果如下:

 

参数缺失情况

我们还是看get请求参数的示例:

    @RequestMapping(value = "/handle8",method = RequestMethod.GET)     @ResponseBody     public String handle8(@RequestParam String username, @RequestParam String product){         return username+ " buy a " + product;     }

如果参数为空串:

如果参数缺失:

啊呀,报错了,这个可不行,我们需要兼容这种参数可以缺失的情况。

 

请求http://localhost:8689/happybks/handle8?username=happybks

请求http://localhost:8689/happybks/handle8

 

这里另外提一下,参数可以不只是String类型,还可以直接用int或Integer类型。

但是需要注意,defaultValue设置数值时,需要是加双引号。

 

运行:

 

@GetMapping注解和@PostMapping注解

@GetMapping注解等价于@RequestMapping(value = "",method = RequestMethod.GET)

例如:

    @GetMapping(value = "/handle10")     @ResponseBody     public String handle10(){         return "OK";     }

运行:

 

最后,我把整个类列出来,方便大家调试:

package com.happybks.pets;  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*;  import java.util.HashMap;  @Controller public class MyController {      @Autowired     PetProperties petProperties;      @RequestMapping(value = "/handle",method = RequestMethod.GET)     public String handle(){         return "handlepage";//petProperties.toString();     }      @RequestMapping(value = "/handle2",method = RequestMethod.GET)     @ResponseBody     public String handle2(){         return "handlepage";//petProperties.toString();     }      @RequestMapping(value = {"/handle3","/petProperties"},method = RequestMethod.GET)     @ResponseBody     public String handle3(){         return petProperties.toString();     }       @RequestMapping(value = "/handle4",method = RequestMethod.POST)     @ResponseBody     public String handle4(@RequestBody String formkvs){         String[] kvs = formkvs.split("&");         final HashMap<String, String> formkvsMap=new HashMap<>();         for(String itemKV:kvs){             String[] fields = itemKV.split("=");             final String key = fields[0];             final String value = fields[1];             formkvsMap.put(key, value);         }         return formkvsMap.get("username") + " buy a " + formkvsMap.get("product");     }      @RequestMapping(value = "/handle5",method = RequestMethod.POST)     @ResponseBody     public String handle5(@RequestBody TradeItem tradeItem){         return tradeItem.getUsername() + " buy a " + tradeItem.getProduct();     }       @RequestMapping(value = "/handle6/{username}/{articleId}",method = RequestMethod.GET)     @ResponseBody     public String handle6(@PathVariable String username, @PathVariable("articleId") String aid){         return username + " write an article, id is " + aid;     }      @RequestMapping(value = "/handle7",method = RequestMethod.POST)     @ResponseBody     public String handle7(@RequestParam String username, @RequestParam String product){         return username+ " buy a " + product;     }      @RequestMapping(value = "/handle8",method = RequestMethod.GET)     @ResponseBody     public String handle8(@RequestParam(value="username", required = false, defaultValue = "no name") String user, @RequestParam(required = false, defaultValue = "apple") String product){         return user+ " buy a " + product;     }      @RequestMapping(value = "/handle9",method = RequestMethod.GET)     @ResponseBody     public String handle9(@RequestParam(value="number", required = false, defaultValue = "6") int num){         return "the number is "+num;     }      @GetMapping(value = "/handle10")     @ResponseBody     public String handle10(){         return "OK";     } } 

 

本文发表于2018年02月22日 10:31
(c)注:本文转载自https://my.oschina.net/happyBKs/blog/1622412,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1879 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1