最全面的SpringMVC教程——设定字符集,json数据序列化,获取请求中的json数据,数据转化,数据校验

148 阅读8分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第29天,点击查看活动详情

前言

本文将为大家对 【SpringMVC教程】核心技术篇 的相关内容详进行介绍,下面具体将对设定字符集返回json数据(序列化)获取请求中的json数据数据转化数据校验等SpringMVC相关核心技术进行详尽介绍~

👉Java全栈学习路线可参考:  【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

👉算法刷题路线可参考:  算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~

↩️本文上接:最全面的SpringMVC教程——视图和模型拆分、重定向与转发、RequestMapping注解、URL模式匹配与传参


一、设定字符集

springmvc内置了一个统一的字符集处理过滤器,我们只要在web.xml中配置即可:

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

二、返回json数据(序列化)

我们经常需要使用ajax请求后台获取数据,而不需要访问任何的页面,这种场景在前后分离的项目当中尤其重要。

这种做法其实很简单,大致步骤如下:

  • 将我们的对象转化为json字符串。
  • 将返回的内容直接写入响应体,不走视图解析器。
  • 然后将Content-Type设置为application/json即可。

为了实现这个目的,我们可以引入fastjson:

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.68</version>
</dependency>
// produces指定了响应的Content-Type
@RequestMapping(value = "getUsers",produces = {"application/json;charset=utf-8"})
@ResponseBody  // 将返回的结果直接写入响应体,不走视图解析器
public String getUsers(){
    List<User> users =  new ArrayList<User>(){{
        add(new User("Tom","2222"));
        add(new User("jerry","333"));
    }};
    return JSONArray.toJSONString(users);
}

测试: 成功!

注意:@ResponseBody能将返回的结果直接放在响应体中,不走视图解析器。

image.png

浏览器中添加插件json viewer可以有如上显示。

image.png

当然springmvc也考虑到了,每次这样写也其实挺麻烦,我们还可以向容器注入一个专门处理消息转换的bean。

这个转化器的作用就是:当不走视图解析器时,如果发现【返回值是一个对象】,就会自动将返回值转化为json字符序列:

<mvc:annotation-driven >
        <mvc:message-converters>
            <bean id="fastjson" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <!-- 这里顺序不能反,一定先写text/html,不然ie下会出现下载提示 -->
    				<value>text/html;charset=UTF-8</value>
					<value>application/json;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
</mvc:annotation-driven>

以后我们的controller就可以写成下边的样子了:

@RequestMapping(value = "getUsersList")
@ResponseBody
public List<User> getUsersList(){
    return   new ArrayList<User>(){{
        add(new User("邸智伟","2222"));
        add(new User("刘展鹏","333"));
    }};
}

当然我们还可以使用一个更加流行的组件jackson来处理,他的工作和fastjson一致

首先需要引入以下依赖:

<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

我们还可以对序列化的过程进行额外的一些配置:

public class CustomObjectMapper extends ObjectMapper {

    public CustomObjectMapper() {
        super();
        //去掉默认的时间戳格式
        configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //设置为东八区
        setTimeZone(TimeZone.getTimeZone("GMT+8"));
        //设置日期转换yyyy-MM-dd HH:mm:ss
        setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 设置输入:禁止把POJO中值为null的字段映射到json字符串中
        configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
        // 空值不序列化
        setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 反序列化时,属性不存在的兼容处理
        getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 序列化枚举是以toString()来输出,默认false,即默认以name()来输出
        configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
    }
}

编写配置文件:

<mvc:annotation-driven>

    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <!-- 自定义Jackson的objectMapper -->
            <property name="objectMapper" ref="customObjectMapper" />
            <property name="supportedMediaTypes">
                <list>
                    <value>text/plain;charset=UTF-8</value>
                    <value>application/json;charset=UTF-8</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>

</mvc:annotation-driven>
<!--注入我们写的对jackson的配置的bean-->
<bean name="customObjectMapper" class="com.ydlclass.CustomObjectMapper"/>

测试: 成功!

三、获取请求中的json数据

在前端发送的数据中可能会如如下情况,Contetn-Type是application/json,请求体中是json格式数据:

image.png

@RequestBody注解可以【直接获取请求体的数据】。

如果我们配置了消息转化器,消息转化器会将请求体中的json数据反序列化成目标对象,如下所示:

@PostMapping("insertUser")
public String insertUser(@RequestBody User user) {
    System.out.println(user);
    return "user";
}

当然,我们可以把消息转化器注解掉,直接使用一个String来接收请求体的内容。

四、数据转化

假如有如下场景,前端传递过来一个日期字符串,但是后端需要使用Date类型进行接收,这时就需要一个类型转化器进行转化。

自定义的类型转化器只支持从requestParam获取的参数进行转化,我们可以定义如下,其实学习spring时我们已经接触过这个Converter接口:

public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd hh,mm,ss");
        try {
            return simpleDateFormat.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}

然后,我们需要在配置文件中进行配置:

<!-- 开启mvc的注解 -->
<mvc:annotation-driven conversion-service="conversionService" />

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean id="stringToDateConverter" class="cn.itnanls.convertors.StringToDateConverter"/>
        </set>
    </property>
</bean>

对于时间类型的处理,springmvc给我们提供了一个比较完善的解决方案,使用注解@DateTimeFormat,同时配合jackson提供的@JsonFormat注解几乎可以满足我们的所有需求。

  • @DateTimeFormat:当从requestParam中获取string参数并需要转化为Date类型时,会根据此注解的参数pattern的格式进行转化。
  • @JsonFormat:当从请求体中获取json字符序列,需要反序列化为对象时,时间类型会按照这个注解的属性内容进行处理。

这两个注解需要加在实体类的对应字段上即可:

// 对象和json互相转化的过程当中按照此转化方式转哈
@JsonFormat(
            pattern = "yyyy年MM月dd日",
            timezone = "GMT-8"
    )
// 从requestParam中获取参数并且转化
@DateTimeFormat(pattern = "yyyy年MM月dd日")
private Date birthday;

处理的过程大致如下:

image.png

五、数据校验

  • JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它包含在 JavaEE 6.0 中。
  • JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证。
Constraint详细信息
@Null被注解的元素必须为 null
@NotNull被注解的元素必须不为 null
@AssertTrue被注解的元素必须为 true
@AssertFalse被注解的元素必须为 false
@Min(value)被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注解的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注解的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min)被注解的元素的大小必须在指定的范围内
@Digits (integer, fraction)被注解的元素必须是一个数字,其值必须在可接受的范围内
@Past被注解的元素必须是一个过去的日期
@Future被注解的元素必须是一个将来的日期
@Pattern(value)被注解的元素必须符合指定的正则表达式

🍀Hibernate Validator 扩展注解

Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解(Hibernate Validator 附加的 constraint):

Constraint详细信息
@Email被注解的元素必须是电子邮箱地址
@Length被注解的字符串的大小必须在指定的范围内
@NotEmpty被注解的字符串的必须非空
@Range被注解的元素必须在合适的范围内

🍀Spring MVC 数据校验

Spring MVC 可以对表单参数进行校验,并将结果保存到对应的【BindingResult】或 【Errors 】对象中。

要实现数据校验,需要引入已下依赖:

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.9.Final</version>
</dependency>

并在实体类加上特定注解:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserVO {

    @NotNull(message = "用户名不能为空")
    private String username;

    @NotNull(message = "用户名不能为空")
    private String password;

    @Min(value = 0, message = "年龄不能小于{value}")
    @Max(value = 120,message = "年龄不能大于{value}")
    private int age;

    @JsonFormat(
            pattern = "yyyy-MM-dd",
            timezone = "GMT-8"
    )
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Past(message = "生日不能大于今天")
    private Date birthday;

    @Pattern(regexp = "^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$", message = "手机号码不正确")
    private String phone;

    @Email
    private String email;
}

在配置文件中配置如下内容,增加hibernate校验:

<bean id="localValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>
<!--注册注解驱动-->
<mvc:annotation-driven validator="localValidator"/>

controller使用@Validated标识验证的对象,紧跟着的BindingResult获取错误信息

@PostMapping("insert")
public String insert(@Validated UserVO user, BindingResult br) {
    List<ObjectError> allErrors = br.getAllErrors();
    Iterator<ObjectError> iterator = allErrors.iterator();
    // 打印以下错误结果
    while (iterator.hasNext()){
        ObjectError error = iterator.next();
        log.error("user数据校验错误:{}",error.getDefaultMessage());
    }

    if(allErrors.size() > 0){
        return "error";
    }

    System.out.println(user);
    return "user";
}

注意: 永远不要相信用户的输入,我们开发的系统凡是涉及到用户输入的地方,都要进行校验,这里的校验分为前台校验和后台校验,前台校验通常由javascript来完成,后台校验主要由java来负责,这里我们可以通过spring mvc+hibernate validator完成。


后记

本文呢本文为大家对 【SpringMVC教程】核心技术篇 的相关内容详进行介绍,具体对设定字符集返回json数据(序列化)获取请求中的json数据数据转化数据校验等SpringMVC相关核心技术进行了详尽介绍~

希望本文的内容能够使你有所收获,如果你想继续深入的学习数据结构与算法相关的知识,或想深入的学习Java相关的知识与技术,可以参考:

👉Java全栈学习路线可参考:  【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

👉算法刷题路线可参考:  算法刷题路线总结与相关资料分享,内含最详尽的算法刷题路线指南及相关资料分享~

看完不关注就想跑.gif