使用@RestController,@ExceptionHandler和@Valid,把检验和异常处理从主要业务逻辑里面抽离出来

@Restcontroller登场

spring从4.0开始引入了@Restcontroller,这是对REST的支持,他可以帮我们去掉@ResponseBody这个
所以原本这样的代码,

@Controller
public class SpitteController{ 

    @RequestMapping(value="/{id}", method=RequestMethod.GET)
    public @ResponseBody Spittle spittleById(@PathVariable long id) {
    Spittle spittle = spittleRepository.findOne(id);
    if (spittle == null) { throw new SpittleNotFoundException(id); }
    return spittle;
    }

}

使用@RestController

@RestController
public class SpitteController{ 

    @RequestMapping(value="/{id}", method=RequestMethod.GET)    
    public Spittle spittleById(@PathVariable long id) {
    Spittle spittle = spittleRepository.findOne(id);
    if (spittle == null) { throw new SpittleNotFoundException(id); }
    return spittle;
    }

}

是的,少了一个@ResponseBody这个玩意.感觉也是挺舒服的,如果你这个Controller有几十个方法的话.

关于异常的处理

    @RequestMapping(value="/{id}", method=RequestMethod.GET)
    public @ResponseBody Spittle spittleById(@PathVariable long id) {
     return spittleRepository.findOne(id);
    }   

上面的代码看起来没什么问题.但如果没有这个id呢?返回的内容居然就是个null,那显然不太好.
所以我们需要加点返回信息,最少设置头部的状态为NO_FOUND像下面这样,


    @RequestMapping(value="/{id}", method=RequestMethod.GET)
    public ResponseEntity<Spittle> spittleById(@PathVariable long id) {
        Spittle spittle = spittleRepository.findOne(id);
        HttpStatus status = spittle != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
        return new ResponseEntity<Spittle>(spittle, status);
    } 

如果返回为空,我们返回的状态为HttpStatus.NOT_FOUND,虽然spittle这个为空依然没解决,也没返回什么具体的信息.所以我们该加点内容,例如msg为改ID无效之类的.

@RequestMapping(value="/{id}", method=RequestMethod.GET)
public ResponseEntity<?> spittleById(@PathVariable long id) {
    Spittle spittle = spittleRepository.findOne(id);
    if (spittle == null) {
         Error error = new Error(Error.NOT_FOUND, "Spittle [" + id + "] not found");
         return new ResponseEntity<Error>(error, HttpStatus.NOT_FOUND);
    }
    return new ResponseEntity<Spittle>(spittle, HttpStatus.OK);
}
Error具体如下
public class Error {
private int code;
private String message;
public Error(int code, String message) {
    this.code = code;
    this.message = message;
}
    public int getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
}

ok,我们成功的对没有这个id的时候,返回了一些有用的信息了.但主业务逻辑看起来有点奇怪了,
不知道你注意到了没有,为了能够返回这个信息,我们把函数的返回改成了ResponseEntity<?>
感觉不是很好,所以要调整下,改得像下面这样,是不是还挺好的?函数返回的是 Spittle,不再是ResponseEntity<?> 这玩意了.

@RequestMapping(value="/{id}", method=RequestMethod.GET)
public  Spittle spittleById(@PathVariable long id) {
    Spittle spittle = spittleRepository.findOne(id);
    if (spittle == null) { throw new SpittleNotFoundException(id); }
     return spittle;
}

像上面这样就挺好的,不过这代码是有代价的,我们引入了一个新的玩意throw new SpittleNotFoundException(id);抛了一个异常出来,难道我们要玩自杀?居然抛异常出来?想让服务器奔溃?显然不可以,得找人来处理这个异常,然后返回错误信息才对啊,就在这关键的时候

@ExceptionHandler登场

@ExceptionHandler(SpittleNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public @ResponseBody Error spittleNotFound(SpittleNotFoundException e) {
    long spittleId = e.getSpittleId();
    return new Error(4, "Spittle [" + spittleId + "] not found");
}

他可以捕获我们跑出来的这个SpittleNotFoundException异常.
所以我们的Controller可以改成这样

@RestController
public class SpitteController{ 

    @RequestMapping(value="/{id}", method=RequestMethod.GET)    
    public Spittle spittleById(@PathVariable long id) {
    Spittle spittle = spittleRepository.findOne(id);
    if (spittle == null) { throw new SpittleNotFoundException(id); }
    return spittle;
    }


    @ExceptionHandler(SpittleNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public  Error spittleNotFound(SpittleNotFoundException e) {
        long spittleId = e.getSpittleId();
        return new Error(4, "Spittle [" + spittleId + "] not found");
    }   
}
注意

我们把spittleNotFound前面的@ResponseBody也去掉了.
下面是exception的具体内容,需要注意的是,他继承了RuntimeException

public class SpittleNotFoundException extends RuntimeException {
    private long spittleId;

    public SpittleNotFoundException(long spittleId) {
        this.spittleId = spittleId;
    }

    public long getSpittleId() {
        return spittleId;
    }
}

好了,这是一个异常的处理,但有个问题需要提出的是,这个抛出的异常,只在这个Controller中被处理.很显然在实际的开发中,还是有可能别的Controller中,抛出了一个一样的异常,所以我们需要一个能够统一处理这些异常的类,spring提供了下面的解决方案

@ControllerAdvice登场

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class AppWideExceptionHandler {

    @ExceptionHandler(SpittleNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public  Error spittleNotFound(SpittleNotFoundException e) {
        long spittleId = e.getSpittleId();
        return new Error(4, "Spittle [" + spittleId + "] not found");
    }   

    @ExceptionHandler(DuplicateSpittleException.class)
    public String duplicateSpittleHandler() {
    return "error/duplicate";
    }       
}

ControllerAdvice的作用就是这样,全局的去捕获异常.
这样我们就可以把所有的处理都写在一个类里面了.感觉还是挺好的.


做完前面的工作铺垫,要来让@valid登场了

@Valid登场

@RequestMapping(value="/register", method=POST)
public String processRegistration(@Valid Spitter spitter,Errors errors) {

    if (errors.hasErrors()) {
        throw new HasErrorsException(); 
    }
    ...
    //一些业务操作.
}

我们的经常要求一些字段有大小,长度的的限制等,例如要求用户的密码不低于六位,名字不能为空,也不能多于20个字符等的限制.所以我们需要加多一个@Valid在我们要检验的类前面,来告诉spring,如果这家伙来了,我们要检查他.因为他可能不符合要求.检查出错的结果会在Errors里面,- 需要注意的是,Errors这个要紧跟在声明了@valid的object后面! -
检测的条件,我们需要用到@NotNull和@Size这两个来帮忙.

public class User{

        @NotNull
        @Size(min=5, max=16, message="{username.size}")
        private String username;
        @NotNull
        @Size(min=5, max=25, message="{password.size}")
        private String password;
        @NotNull
        @Size(min=2, max=30, message="{firstName.size}")
        private String firstName;
        @NotNull
        @Size(min=2, max=30, message="{lastName.size}")
        private String lastName;
        @NotNull
        @Email(message="{email.valid}")
        private String email;
}

所有上面用到的msg在我们\resource文件夹新建的ValidationNameMessages.properties 里面,具体内容如下.

firstName.size=
First name must be between {min} and {max} characters long.
lastName.size=
Last name must be between {min} and {max} characters long.
username.size=
Username must be between {min} and {max} characters long.
password.size=
Password must be between {min} and {max} characters long.
email.valid=The email address must be valid.

通过使用@Valid,我们简化了很多检验的操作.最原始的做法如下

@RequestMapping("/signUp")
    @ResponseBody
    public Result saveAccount(HttpServletRequest request,HttpServletResponse response) throws Exception{

    String password =request.getParameter("password");
    String name =request.getParameter("name"); 
    String zone = request.getParameter("zone");
    String code = request.getParameter("code");
    String device =request.getParameter("device");
    Result result = accountService.checkcode(phone, zone, code, password, device);

     if(password ==null || password.size<6||password.size>20)
     {   
        result = "{" 错误信息"}"; 
        response.setContentType("application/json"); 
        response.setCharacterEncoding("utf-8");      
        Writer writer = response.getWriter();
        writer.write(result);
        writer.close();
        return false;
        }
     }
     if(name ==null || name.size<6 || name.size>20)
     {
        ................
     }

      .........一堆别的的检验 ........

}

和上面的相比较,是不是好看了?是不是?
好了…先写到这里,这鬼玩意已经写了我快两个小时了..
真的写篇文章真不容易.你们看完觉得可以得给个评论大叫一声好啊!!!!!

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值