Spring Boot2--Web开发(二)


注解@PathVariable用于从请求URL中获取参数并映射到方法参数中,如下代码:

@Controller
@RequestMapping("/user/{id}")
public class HelloworldController {

	@Autowired
	UserService userService;
	
	@GetMapping(path="/{type}/get.json")
	public @ResponseBody User getUser(@PathVariable Long id,@PathVariable Integer type){
		return userService.getUserById(id);
	}
}

    符号{}中的变量名与方法中的参数名一一对应,如果不想对应,如路径中的名字是id,方法签名是userId,则可以使用@PathVariable(“id”)long userId来对应。

    通常情况下,Java的的编译代码的时候,会将参数名称也编译到类字节码里,因此春季会根据名字匹配自动映射如果上述例子中出现如下错误:

    出现意外错误(type = Internal Server Error,status = 500)。

    参数类型[java.lang.Long]的名称不可用,参数名称信息也未在类文件中找到。 

    则表明你的编译环境未将调试信息加入类中,建议将编译器改成默认设置。以Eclipse中为例,选择工程,右键选择属性,找到的Java编译器,在类文件生成选项中勾选除内联终于块外的所有选项,如图所示:

    Spring也支持URL中的矩阵变量,所谓矩阵变量,就是出现在URL片段中,通过“;”分割的多个变量,比如/user/id=123;status=1/update.json。

JavaBean中的接受HTTP  参数

    HTTP提交的参数可以映射到方法参数上,按照名称来映射,比如一个请求/javaBean/update.json?name=abc&id=1,将会映射到如下方法:

@GetMapping(path="/update.json")
public @ResponseBody User getUser(Integer id,String type){
	return userService.getUserById(id);
}

    可以通过@RequestParam来进一步限定HTTP参数到控制器方法的映射关系,RequestParam支持如下属性:

    value:指明HTTP参数的名称。

    需要:布尔类型,声明此参数是否必须有,如果HTTP参数里没有,则会抛出400错误。

    defaultValue:字符类型,如果HTTP参数没有提供,可以指定一个默认字符串,Spring类型转换为目标类型,如上一个例子,我们可以提供默认参数:

@GetMapping(path="/update.json")
public @ResponseBody User getUser(@RequestParam(value = "id", required = true, defaultValue = "12") Integer id,String type) {
	return userService.getUserById(id);
}

    可以将HTTP参数转为JavaBean对象,HTTP参数的名字对应到POJO的属性名。通常,HTTP提交了多个参数,Spring支持按照前缀自动映射到不同的对象上。简单来说,Spring有如下表所示的HTTP参数到JavaBean中的映射规则:

示例

解释

名称

的对象名称属性

order.name

的对象顺序属性的名称属性

细节[0]。名称

对象的详细信息属性,要求详情是个数组或者List  (不能是Set  ,因为Set  不具备根据索引取值的功能),details [0]  表示详细信息属性的第一个元素

@RequestBody  接受JSON

    控制器方法带有@RequestBody注解的参数,意味着请求的HTTP消息体的内容是一个JSON,需要转化为注解指定的参数类型.Spring Boot默认使用Jackson来处理反序列化工作。

MultipartFile

    通过MultipartFile来处理文件上传,MultipartFile提供了以下方法来获取上传的文件信息:

    getOriginalFilename:获取上传的文件名字。

    getBytes:获取上传文件内容,转为字节数组。

    getInputStream:获取一个InputStream。

    isEmpty:文件上传为空,或者就没有文件上传。

    getSize:文件上传的大小。

    transferTo(File dest):保存上传文件到目标文件系统。

    如果同时上传多个文件,则使用MultipartFile数组类来接受多个文件上传。

Spring Boot中可以通过配置文件application.properties对上传文件进行限定,默认为如下配置:

spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=0
spring.servlet.multipart.location=
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.resolve-lazily=false

    参数enabled默认为true,即允许上传,file-size-threshold限定了当上传的文件超过一定长度时,就写到临时文件里。者有助于上传文件不占用过多的内存,单位时MB或者KB,默认是0,即不限定阀值。位置指的是临时文件的存放目录,如果不设定,则网络服务器提供一个临时目录。

最大文件大小属性指定了单个文件的最大长度,默认是1MB,最大请求大小的属性说明单次HTTP请求上传的最大长度,默认是10MB。解决,懒洋洋地表示当文件和参数被访问的时候再解析成文件。

    如果上传大文件失败,则需要检查是不是因为Spring Boot对文件的限定过小造成的。另一方面,有些Spring Boot应用设置了代理服务器,比如设置了Apache,也需要检查代理服务器是否支持大文件上传,是否对超时做了设定。

@ModelAttribute

    注解的ModelAttribute通常作用做控制器的某个方法上,此方法会首先被调用,并将方法结果作为模型的属性,然后再调用对应的控制器处理方法。

@ModelAttribute
public void findUserById(@PathVariable Long id,Model model){
	model.addAttribute("user", userService.getUserById(id));
}
	
@GetMapping(path="/{id}/get.json")
public @ResponseBody String getUser(Model model) {
	System.out.println(model.containsAttribute("user"));
	return "success";
}

    对于HTTP请求,modelattribute / 1 / get.json,会先调用findUserById方法取得user,并添加到模型里。使用ModelAttribute通常可以用来向一个Controller中需要的公共模型添加数据。

    如果findUserById仅仅添加一个对象到模型中,则可以改写成如下形式:

@ModelAttribute
public void findUserById(@PathVariable Long id){
	userService.getUserById(id);
}

    这样,返回到对象自动添加到Model中,相当于调用model.addAttribute(user)。

@InitBinder

    将HTTP参数绑定到JavaBean的对象中,其实春天框架是通过WebDataBinder类实现这种绑定到,所以,可以在控制器中用注解@InitBinder声明一个方法,来自己扩展绑定到特性,比如:

@InitBinder
public void findUserById(WebDataBinder binder){
	binder.addCustomFormatter(new DateFormatter("yyyy-mm-dd"));
}
	
@RequestMapping(path="/date")
public @ResponseBody void printDate(Date d) {
	System.out.println(d);
	return;
}

    当需要绑定到一个Date类型的时候,如上述代码所示,则采用“yyyy-MM-dd”格式,比如用户访问databind / date?d = 2001-1-1。

验证框架

    Spring Boot支持JSR-303,Bean验证框架,默认实现用的是Hibernate验证器。在Spring MVC中,只需要使用@Valid注解标注中方法参数上,Spring Boot即可对参数对象进行校验,校验结果放在BindingResult对象中。

    JSR-303定义了一系列注解用来验证的Bean的属性,常用的有如下几种:

@空值

验证对象是否为空

@NotNull

验证对象不为空

@NotBlank

验证字符串不为空或者不为空字符串比如“”  “”  都会验证失败

@不是空的

验证对象不为null  ,或者集合不为空

@Size(MIN =,最大=)

验证对象长度,可支持字符串,集合

@长度

字符串大小

@Min

验证数字是否大于等于指定的值

@Max

验证数字是否小于等于指定的值

@digits

验证数字是否符合指定格式,如@digits(整数= 9,分数= 2)

@范围

验证数字是否在指定的范围内,如@range(分钟= 1,最大值为100)

@电子邮件

验证是否为邮件格式,为则不做校验

@图案

验证字符串对象是否符合正则表达式的规则

    通常,不同的业务逻辑会有不同的验证逻辑,比如对于WorkInfoForm来说,当更新的时候,id必须不为null,但增加的时候,id必须是null。

    JSR-303定义了group概念,每个校验注解都必须支持。校验注解作用在字段上的时候,可以指定一个或多个组,当Spring Boot校验对象的时候,也可以指定校验的上下文属于哪个组。这样,只有组匹配的时候,校验注解才能生效。上面的WorkInfoForm定义id字段校验可以更改为如下内容:

public class WorkInfoForm{
	//定义一个类,更新时校验组
	public interface Update{}
	//定义一个类,添加时校验组
	public interface Add{}
		
	@NotNull(groups={Update.class})
	@Null(groups={Add.class})
	Long id;
}

    这段代码表示,当校验上下文为Add.class的时候,@ null生效,ID需要为空才能校验通过;当校验上下文为Update.class的时候,@ NotNull生效,ID不能为空。

MVC  中使用@Validated

    在控制器中,只需要给方法参数加上@Validated即可触发一次校验。

@RequestMapping("/addworkinfo.html")
public void addWorkInfo(@Validated({WorkInfoForm.Add.class}) WorkInfoForm woekInfo,BindingResult result) {
	if(result.hasErrors()){
		List<ObjectError> list = result.getAllErrors();
		FieldError error = (FieldError) list.get(0);
		System.out.println(error.getObjectName()+","+error.getField()+","+error.getDefaultMessage());
		return;
	}
	return;
}

    此方法可以接受HTTP参数并映射到WorkInfoForm对象,此参数使用了@Validated注解,将触发Spring到校验,并将验证结果存放到BindingResult对象中。这里,验证注释使用了校验的上下文WorkInfoForm.Add .class,因此,整个校验将按照Add.class来校验。

    BindingResult包含了验证结果,提供了如下方法:

    hasErrors,判断验证是否通过。

    getAllErrors,得到所有的错误信息,通常返回的是FieldError列表。

    如果控制器参数未提供BindingResult对象,则Spring MVC将抛出异常。

自定义校验

    JSR-303提供的大部分校验注解已经够用,也允许定制校验注解,比如在WorkInfoForm类中,我们新增一个加班时间:

@WorkOverTime
int workTime;

    属性workTime使用了注解@WorkOverTime,当属性值超过max值的时候,将会验证失败.WorkOverTime跟其他注解差不多,但提供了@Constraint来说明用什么类作为验证注解实现类,代码如下:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Constraint(validatedBy = {WorkOverTimeValidator.class})
@Documented
@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface WorkOverTime {
	String message()  default "加班时间过长,不能超过{max}小时";
	int max() default 5;
	Class<?> [] groups() default {};
	Class<? extends Payload>[] payload() default {};
}

    @Constraint注解说明用什么类来实现验证,我们将创建一个WorkOverTimeValidator。来进行验证注解必须提供如下信息:

    message,用于创建错误信息,支持表达式,如“错误,不能超过(max)小时”。

    groups,验证规则分组,比如新增和修改的验证规则不一样,分为两个组,验证注解必须提供。

    有效载荷,定义了验证的有效负荷。

    WorkOverTimeValidator必须实现ConstraintValidator接口初始化方法及验证方法isValid:

package com.scg.springboot;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class WorkOverTimeValidator implements ConstraintValidator<WorkOverTime, Integer>{
	WorkOverTime work;
	int max;
	public void initialize(WorkOverTime work){
		//获取注解的定义
		this.work = work;
		max = work.max();
	}
	@Override
	public boolean isValid(Integer value, ConstraintValidatorContext context) {
		if(value==null){
			return true;
		}
		return value<max;
	}
}

WebMvcConfigurer

    WebMvcConfigurer是用来全局定制化Spring Boot的MVC特性。开发者可以通过实现WebMvcConfigurer接口来配置应用的MVC全局特性。

package com.scg.springboot;

import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfigurer implements WebMvcConfigurer{

	//拦截器
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		WebMvcConfigurer.super.addInterceptors(registry);
	}
	//跨域访问配置
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		//允许所有跨域访问
		registry.addMapping("/**");
		//允许来自domain2.com的跨域访问,并且限定访问路径为/api、方法是POST或者GET。
		registry.addMapping("/api/**")
			.allowedOrigins("http://domain2.com")
			.allowedMethods("POST","GET");
	}
	//格式化
	@Override
	public void addFormatters(FormatterRegistry registry) {
		/**
		 * 将HTTP请求映射到Controller方法的参数上后,Spring会自动进行类型转化。对于日期类型的参数,
		 * Spring默认并没有配置如何将字符串转为日期类型。为了支持可按照指定格式转为日期类型,需要添加
		 * 一个DateFormatter类:
		 */
		registry.addFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
	}
	//URI到视图到映射
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		WebMvcConfigurer.super.addViewControllers(registry);
	}
}

使用杰克逊

    在MVC框架中,Spring Boot内置了Jackson来完成JSON的序列化和反序列化。在Controller中,方法注解为@ResponseBody,自动将方法返回的对象序列化成JSON。如果想自己全局自定义一个ObjectMapper来代替默认的,则可以使用Java Config,联合使用@Bean,代码如下:

package com.scg.springboot.controller;

import java.text.SimpleDateFormat;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
public class JackSonConf {

	@Bean
	public ObjectMapper getObjectMapper(){
		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
		return objectMapper;
	}
}

    上述Java Config会使用Spring Boot使用自定义的Jackson来序列化而非默认配置的。以下是一个用来获取当前时间的请求:

package com.scg.springboot.controller;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/json")
public class JackSonController {

	@GetMapping("/user/{id}.json")
	public @ResponseBody Map<String,Date> now(){
		Map<String,Date> mp = new HashMap<String,Date>();
		mp.put("time", new Date());
		return mp;
	}
}

    当用户访问now.json的时候,会得到如下输出:

{“time”:“2018-10-23 20:50:05”}

重定向转发

        有些情况下,Controller会返回客户端一个HTTP Redirect重定向请求,希望客户端按照指定地址重新发起一次请求,比如客户端登录成功后,重定向到后台系统首页。再比如客户端通过POST提交了一个名单,可以返回一个重定向请求到此订单明显的请求地址。这样做的好处是,如果用户再次刷新页面,则访问的是订单详情地址,而不会再次提交订单。

    Controller中重定向可以返回以“redirect:”为前缀的URI:

@RequestMapping("/order/saveorder.html")
public String saveOrder(Order order){
	Long orderId = service.addOrder(order);
	return "redirect:/order/detail.html?orderId="+orderId;
}

    还可以在ModelAndView对象中设置带有“重定向:”前缀的URI:

ModelAndView view = new ModelAndView("redirect:/order/detail.html?orderId="+orderId);

或者直接使用RedirectView的的类:

RedirectView view = new RedirectView("/order/detail.html?orderId="+orderId);

Spring MVC也支持forward前缀,用来在Controller执行完毕后,再执行另外一个Controller的方法。

@RequestMapping("/bbs")
public String index(){
	//forward 到 module方法
	return "forward:/bbs/module/1-1.html";
}
@RequestMapping("/bbs/moudle/{type}-{page}")
public ModelAndView module(@PathVariable int type,@PathVariable int page){
	……
}

    对所有访问/ bbs的请求,都会转到模块方法,因为转发的URL是/bbs/module/1-1.html,正好匹配模块方法的@RequestMapping的定义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值