转载: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. C# WinForm TextBox添加水印效果

    1.新建项目添加WatermarkTextBox类: using System; using System.Collections.Generic; using System.Text; using ...

  2. angular的post请求,SpringMVC后台接收不到参数值的解决方案

    这是我后台SpringMVC控制器接收isform参数的方法,只是简单的打出它的值: @RequestMapping(method = RequestMethod.POST) @ResponseBod ...

  3. hibernate的id生成策略

    欢迎转载,请注明出处http://www.cnblogs.com/shizhongtao/p/3436523.html 一.xml配置方式的id生成 <id name="id" ...

  4. c++ primer (5)1

    第一章 1.包含来自标准库的头文件用<>,不属于标准库用"". 2.默认情况,读cin会刷新cout:程序非正常终止时也会刷新cout. 3.默认情况,cerr不缓冲, ...

  5. 随机森林之Bagging法

    摘要:在随机森林介绍中提到了Bagging方法,这里就具体的学习下bagging方法. Bagging方法是一个统计重采样的技术,它的基础是Bootstrap.基本思想是:利用Bootstrap方法重 ...

  6. 视酷即时通讯系统应用源码 V1.0

    视酷即时通讯系统(原创),成熟稳定,拥有和微信一样强大的功能不再是梦,节省几个月研发时间迅速融合进项目中: 1.首家支持聊天室群聊 2.支持和微信一样的语音聊天,可以显示时长.未读状态,自动轮播未读语 ...

  7. [C#]AccessUtils

    关键代码: using System; using System.Data; using System.Data.OleDb; namespace CSharpUtilHelpV2 { /// < ...

  8. grails导入excel

    grails导入excel,意思是说从excel表中读取多条数据,批量写入数据库. 有2种方案,1是使用grails的excel插件,2是调用java代码使用POI等第三方java控件. 今天比较累, ...

  9. spring-cloud-feign案例

    主要依赖 <dependencyManagement> <dependencies> <dependency> <groupId>org.springf ...

  10. 解决ListView滑动时卡的问题,实现异步加载图片解决

    ListView是最为常见的空间之一,现在的应用的呈现形式大多数都需要用到ListView来呈现,以列表的方式最直观最便于操作. 那么在使用的过程中大家一定使用adapter适配器来匹配这个ListV ...