转载:http://coderbee.net/index.php/java/20140719/959

项目组用了 Spring MVC 进行开发,觉得对里面的使用方式不是很满意,就想,如果是我来搭建开发环境,我会怎么做?下面就是我的想法,只关注于 MVC 的 View 层。

一、统一的响应格式

现在基本上都是用 ajax 来调用后台接口,拿到 json格式的数据再展示,有的人直接返回数据,却没有考虑异常的情况,我觉得返回的报文里必须包含表示可能的异常信息的数据和业务响应数据。我定义了下面这个类来表示报文格式:

/**
* 统一的 HTTP 响应格式。<br/>
* code 为 "ok" 表示业务调用成功,否则是失败的错误码,如果有多个则以逗号分隔。<br/>
* data 是业务数据,如果失败了则是 null。
*
* @author http://coderbee.net
*
*/
public class RespBody {
public static final String OK_CODE = "ok";
private final String code;
private final Object data; private static final RespBody OK = new RespBody(OK_CODE, null); private RespBody(String code, Object data) {
this.code = code;
this.data = data;
} public static RespBody ok() {
return OK;
} public static RespBody ok(Object data) {
return new RespBody("ok", data);
} public static RespBody error(String code) {
return new RespBody(code, null);
} public static RespBody error(String code, Object msg) {
return new RespBody(code, msg);
} public String getCode() {
return code;
} public Object getData() {
return data;
}
}

这个类提供了一些静态方法来快速构建响应报文,这也是很重要的一个设计:用静态工厂方法而不是构造函数。

这里的 code 不应该是直接的错误提示信息,应该只是简单的错误编码,这样不同的客户端都可以调用这个 API,然后再根据错误编码、客户端语言和自己的客户端特性选择合适的错误提示信息和提示方式。

二、统一的异常处理

很多人都不考虑异常的情况,导致异常栈直接抛到响应里,这是不友好也不安全的。统一的异常处理是必须的。

我定义了一个 BaseController

@Controller
public class BaseController {
protected final static Logger logger = LoggerFactory
.getLogger("controller"); @ResponseBody
@ExceptionHandler(Exception.class)
public RespBody exceptionHandler(Exception ex) {
return RespBody.error("exception", ex.getMessage());
}
}

它的作用很简单,就是定义了一个统一的异常处理逻辑。Spring MVC对异常处理的逻辑很好,如果某个Controller类没有提供带 @ExceptionHandler 注解的方法,则会查找父类是否有这种方法,所以继承自这个 BaseController 的 Controller 都自动获得异常处理能力。

三、统一的参数校验

参数校验是必须的,而且必须放在服务器端来做,客户端的校验都是可以绕过的

Spring MVC 当然也支持参数校验,在 Spring MVC 的配置文件里加入 <mvc:annotation-driven /> 即可以开启注解校验。

但 Spring MVC 的参数校验有些局限:

  • 不支持对基本类型和 String 类型的参数进行校验,也就是只支持对 POJO 校验,这个非常不友好,如果一个接口只有很少的几个参数都必须定义一个 POJO 是很恼人的,要么就得手动校验,写一些 if 分支;
  • 每个POJO后面都得有一个 BindingResult 的参数,作为对 POJO 的校验结果。

而且在每个方法里都必须对 BindingResult 进行检测,来判断参数是否合法。

在 AOP 里进行参数校验

借助 Spring 对 AOP 的支持,我们可以在 AOP 里对请求的 Controller 的方法进行拦截,做参数校验,如果校验不合格,则直接返回(因为我们已经有了统一的响应格式)。

在 AOP 里,我们可以用 Hibernate-Validator 进行手动校验,而不是通过 Spring-MVC 进行校验,这样我们就不需要在每个 POJO 后面放一个 BindingResult 参数,且 Hibernate-Validator 支持对基本类型和 String 类型的参数进行校验。

下面的代码是在 Hibernate-Validator-4.2.0-Final、validation-api-1.0.0-GA 下测试的:

首先定义一个 BindingResultHandler 类,它的方法 validate 校验请求的 Controller 的方法的参数是否合格,如果合格则继续调用业务逻辑,否则返回错误提示。

public class BindingResultHandler {

    public Object validate(ProceedingJoinPoint pjp) throws Throwable {
Object target = pjp.getTarget(); MethodSignature joinPointObject = (MethodSignature) pjp.getSignature();
Method method = joinPointObject.getMethod(); MethodValidator validator = Validation
.byProvider(HibernateValidator.class).configure()
.buildValidatorFactory().getValidator()
.unwrap(MethodValidator.class); Set<MethodConstraintViolation<Object>> violations = validator
.validateAllParameters(target, method, pjp.getArgs(),
new Class[] {}); if (!violations.isEmpty()) {
StringBuilder sb = new StringBuilder(128);
for (ConstraintViolation<Object> violation : violations) {
sb.append(',').append(violation.getMessage());
}
return RespBody.error(sb.substring(1), "param validation failed .");
} return pjp.proceed();
}
}

AOP 配置:

<bean id="bindingResultHandler" class="net.coderbee.demo.controller.validation.BindingResultHandler" />
<aop:config>
<aop:aspect id="aspectBindingResult" ref="bindingResultHandler">
<aop:pointcut id="bindingResultHandlerPointcut"
expression="execution(public * net.coderbee.demo.controller..*Controller.*(..))" /> <aop:around method="validate" pointcut-ref="bindingResultHandlerPointcut" />
</aop:aspect>
</aop:config>

这样在 Controller 里就只需做注解不能完成的校验了。

@Controller
public class UserController extends BaseController {
@ResponseBody
@RequestMapping(value = "/test/valids")
public RespBody valids(@Valid User user, @Valid Address address) { return RespBody.ok(user);
}
}

这样的代码会简洁很多。

Spring MVC 与 web开发的更多相关文章

  1. 基于Spring MVC的Web应用开发(三) - Resources

    基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...

  2. Spring Boot 2.X(三):使用 Spring MVC + MyBatis + Thymeleaf 开发 web 应用

    前言 Spring MVC 是构建在 Servlet API 上的原生框架,并从一开始就包含在 Spring 框架中.本文主要通过简述 Spring MVC 的架构及分析,并用 Spring Boot ...

  3. [翻译]Spring MVC RESTFul Web Service CRUD 例子

    Spring MVC RESTFul Web Service CRUD 例子 本文主要翻译自:http://memorynotfound.com/spring-mvc-restful-web-serv ...

  4. spring mvc构建WEB应用程序入门例子

    在使用spring mvc 构建web应用程序之前,需要了解spring mvc 的请求过程是怎样的,然后记录下如何搭建一个超简单的spring mvc例子. 1) spring mvc的请求经历 请 ...

  5. 使用IDEA和gradle搭建Spring MVC和MyBatis开发环境

    1. 概述 Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具. 它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐 ...

  6. Spring + Spring MVC + Hibernate项目开发集成(注解)

    在自己从事的项目中都是使用xml配置的方式来进行的,随着项目的越来越大,会发现配置文件会相当的庞大,这个不利于项目的进行和后期的维护.于是考虑使用注解的方式来进行项目的开发,前些日子就抽空学习了一下. ...

  7. Spring基础系列-Web开发

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9996902.html SpringBoot基础系列-web开发 概述 web开发就是集成 ...

  8. 译:7.使用Spring MVC服务Web内容

    本指南向您介绍了使用Spring创建“hello world”网站的过程.阅读原文:Serving Web Content with Spring MVC 1. 你将会构建什么? 您将构建一个具有静态 ...

  9. 跟我学Spring Boot(三)Spring Boot 的web开发

    1.Web开发中至关重要的一部分,Web开发的核心内容主要包括内嵌Servlet容器和SpringMVC spring boot  提供了spring-boot-starter-web 为web开发提 ...

随机推荐

  1. MVC 生成PDf表格并插入图片

    最近做的项目中有一个功能,将最终的个人信息生成PDF表格,并插入图片.对于没接触过的程序员来说回一片茫然,网上有多种生成PDf的方法,我给大家介绍一下我认为比较简单,好操作的一种. iTextShar ...

  2. 第二节 hibernate session介绍以及session常用方法介绍

    原创地址:http://www.cnblogs.com/binyulan/p/5628579.html Session是java应用程序和hibernate框架之间的一个主要接口.它是从持久化服务中剥 ...

  3. Java+FlexPaper+swfTools 文档在线预览demo

    1.概述 主要原理 1.通过第三方工具openoffice,将word.excel.ppt.txt等文件转换为pdf文件 2.通过swfTools将pdf文件转换成swf格式的文件 3.通过FlexP ...

  4. flex&bison 1

    .   {ECHO;}-----单独的flex使用中有效 .   { yyerror();}--------flex和bison交叉使用,即使不调用yyerror函数,也会报错的 error: syn ...

  5. [android]netd与NetworkManagementService初印象

    [功能]Netd是什么,主要负责什么功能 为什么这次会接触Netd主要是因为在设置防火墙时候碰到了.关于Netd可以干什么可以从Netd的源码中CommandListener中得到答案.按照我的理解, ...

  6. input内容改变触发事件,兼容IE

    <html> <head> <script type="text/javascript"> window.onload = function() ...

  7. oracle pl/sql的操作大全

    --删除该用户及下面的所有关联 DROP USER fspdrs CASCADE; --创建一个用户 create user fspdrs identified " default tabl ...

  8. Spark Streaming揭秘 Day32 WAL框架及实现

    Spark Streaming揭秘 Day32 WAL框架及实现 今天会聚焦于SparkStreaming中非常重要的数据安全机制WAL(预写日志). 设计要点 从本质点说,WAL框架是一个存储系统, ...

  9. Spark Streaming揭秘 Day16 数据清理机制

    Spark Streaming揭秘 Day16 数据清理机制 今天主要来讲下Spark的数据清理机制,我们都知道,Spark是运行在jvm上的,虽然jvm本身就有对象的自动回收工作,但是,如果自己不进 ...

  10. Oracle 执行计划说明

    生成SQL的执行计划是Oracle在对SQL做硬解析时的一个非常重要的步骤,它制定出一个方案告诉Oracle在执行这条SQL时以什么样的方式访问数据:索引还是全表扫描,是Hash Join还是Nest ...