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

2018-02-27 11:35:17来源:oschina作者:HappyBKs人点击

分享

想了想有必要把控制器的一些内容放在系列里一起写一下,虽然之前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文件中加入依赖坐标:



org.springframework.boot
spring-boot-starter-thymeleaf
1.5.10.RELEASE

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


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



handlepage.html的内容如下:






handle


HappyBKs at OSC


thymeleaf


spring boot start




注意,刚才控制器类的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 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 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";
}
}

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台