专业编程基础技术教程

网站首页 > 基础教程 正文

Spring Boot2 Web开发(二) springboot开发web项目

ccvgpt 2024-10-16 08:24:06 基础教程 5 ℃

“早成者未必有成,晚达者未必不达。不可以年少而自恃,不可以年老而自弃。”


验证框架

Spring Boot2 Web开发(二) springboot开发web项目

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

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

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

JSR-303定义了group概念,每个校验注解都必须支持。校验注解作用在字段上的时候,可以指定一个或多个group,当Spring Boot校验对象的时候,也可以指定校验的上下文属于哪个group。这样,只有group匹配的时候,校验注解才能生效。上面的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

在Controller中,只需要给方法参数加上@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对象中。这里,Validated注解使用了校验的上下文WorkInfoForm.Add.class,因此,整个校验将按照Add.class来校验。

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

hasErrors,判断验证是否通过。

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

如果Controller参数未提供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,验证规则分组,比如新增和修改的验证规则不一样,分为两个组,验证注解必须提供。

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

WorkOverTimeValidator必须实现ConstraintValidator接口initialize方法及验证方法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);
	}
}

使用JackSon

在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”}

Redirect和Forward

有些情况下,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中设置带有“redirect:”前缀的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的请求,都会forward到module方法,因为forward的URL是/bbs/module/1-1.html,正好匹配module方法的@RequestMapping的定义。

历史文章推荐:

Spring Boot2 Web开发(一)

Spring Boot2 热部署

Tags:

最近发表
标签列表