RESTful基础
- RESTful是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用xml格式定义或JSON格式定义.RESTful适用于移动互联网厂商作为业务接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增,变更,删除所调用资源
- RESTful的设计风格的典型表现是:将某些唯一的请求参数的值放在URL中,使之成为URL的一部分
- 特别要注意的是,RESTful只是一种设计风格,并不是一种规定,也没有明确的或统一的执行方式
- RESTful建议根据希望实现的数据操作不同而使用不同的请求方式
- 查询:使用GET请求
- 新增:使用POST请求
- 修改:使用PUT请求
- 删除:使用DELETE请求
- 在RESTful风格的URL中,大多是包含了某些请求参数的值,在使用SpringMVC框架时,当需要设计这类URL时,可以使用 {名称} 进行占位,并在处理请求的方法的参数列表中,使用@PathVariable注解请求参数,即可将占位符的实际值注入到请求参数中
@GetMapping("/{id}/info.do")
public UserVO info(@PathVariable Long id) {
System.out.println("即将查询 id = " + id + " 的用户的信息……");
UserVO userVO = new UserVO(); userVO.setUsername("chengheng"); userVO.setPassword("1234567890");
userVO.setEmail("chengheng@qq.com");
return userVO;
}
如果@GetMapping参数部分的占位符和方法中的参数名称不一致的时候,需要在@PathVariable注解中配置占位符中的名称:
@GetMapping("/{userId}/info.do")
public UserVO info(@PathVariable("userId") Long id) {
// ...
}
在使用{}格式的占位符时,还可以结合正则表达式进行匹配,如果请求URL不符合正则表达式则会出现404,使用完全不冲突的正则表达式是可以同时存在的
@GetMapping("/{id:[0-9]+}/info.do")
public UserVO info(@PathVariable Long id) {
System.out.println("即将查询 id = " + id + " 的用户的信息……");
// ...
}
@GetMapping("/{username:[a-zA-Z]+}/info.do")
public UserVO info(@PathVariable String username) {
System.out.println("即将查询 用户名 = " + username + " 的用户的信息……");
// ...
}
还可以存在不使用正则表达式,但是URL格式几乎一样的配置:
@GetMapping("/list/info.do")
public UserVO list() {
System.out.println("即将查询 用户的列表 的信息……");
// ...
}
执行时,如果使用/user/list/info.do,则会匹配精准匹配最高的方法,不会与正则表达式部分冲突,使用占位符的做法并不影响精准匹配的路径
响应正文的结果类型
当响应正文时,只要方法的返回值是自定义的数据类型,SpringMVC框架就一定会调用jackson-databind中的转换器,就可以将结果转换为JSON格式的字符串,通常,我们会定义一个通用的数据类型,无论是哪个控制器的哪个处理请求的方法,最终都将返回此类型
public class JsonResult<T>{
private Integer state;
private String message;
private T data;
//将构造方法设为私有的,防止随便创建对象
private JsonResult(){}
//创建成功和失败两种结果的静态方法
public static <T> JsonResult<T> ok (T data){
JsonResult<T> jsonResult = new JsonResult<>();
jsonResult.setState(State.OK.getValue);
jsonResult.setData(data);
return jsonResult;
}
public static JsonResult ok(){
return ok(null);
}
public static JsonResult<Void> fail (State state,String message){
JsonResult<Void> jsonResult = new JsonResult<>();
jsonResult.setState(state.getValue);
jsonResult.setMessage(mesaage);
return jsonResult;
}
//提供getter和setter方法
public Integer getState(){
return state;
}
public void setState(Integer state){
this.state = state;
}
public String getMessage(){
return message;
}
public void setMessage(String message){
this.message = message;
}
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
}
再定义一个枚举类,用于穷举所有错误或成功的情况,因为如果state状态码使用常量的话,那么别人在调动ok或fail方法的时候可以直接传值,与设计返回类型类的初衷不符,所以使用枚举类型,即使别人调用这两个方法,也只能传枚举类中的数,无法传入别的参数:
public enum State{
OK(200),
ERR_USERNAME(201),
ERR_PASSWORD(202);
private Integer value;
State(Integer value){
this.value = value;
}
public Integer getValue(){
return value;
}
}
统一处理异常
1.SpringMVC框架提供了统一处理异常的机制,使得特定种类的异常对应一段特定的代码,后续,当天蝎代码时,无论在任何位置,都可以将异常直接抛出,由统一处理异常的代码进行处理即可
2.统一处理异常,需要自定义方法对异常进行处理,关于此方法要添加:
-注解:必须添加@ExceptionHandler注解
-访问权限:应该是公有的
-返回值类型:可以设置统一的返回值类型
-方法名称:自定义
-参数列表:必须包含1个异常类型的参数,并且可按需添加HttpServletRequest,HttpServletResponse等少量特定的类型的参数,不可以随意添加参数
@ExceptionHandler
public String handleException(NullPointerException e) {
return "Error, NullPointerException!";
}
注意:该处理异常的代码,只能作用域当前控制器类中各个处理请求的方法,对其他控制器类的代码不产生任何英雄,也无法处理其他控制器总处理请求时出现的异常!
所以,为了保证更合理的处理异常,应该将异常处理的代码放在专门的类中,在此类上添加@RestControllerAdvice注解,因为是要响应正文的方式
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public String handleException(NullPointerException e) {
return "Error, NullPointerException!";
}
}
处理的方法参数中的异常类型,就是SpringMVC框架将统一处理的异常类型,与try{}catch(){}相同的是,不能同时有2个或多个方法处理相同的Exception,如果同时存在2个方法,分别处理两个异常时允许的,也可以处理两个子类异常,然后再处理Throwable异常,但是,最大的要放在最后面,并且在处理这个最大的异常的时候,最好要在控制台打印输出异常信息甚至是跟踪信息,以方便我们改错
@ExceptionHandler注解还可用于配置被注解的方法能够处理的异常的类型,其效力的优先级高于在方法的参数上指定异常类型
建议为每一个@ExceptionHandler配置注解参数,在注解参数中指定需要处理异常的类型,而处理异常的方法的参数类型只需要包含@ExceptionHandler配置的类型即可,甚至可以是Throwable
ExceptionHandler({ NullPointerException.class, ClassCastException.class })
public String handleNullPointerException(Throwable e) {
return "Error, NullPointerException or ClassCastException!";
}
@ExceptionHandler(NumberFormatException.class)
public String handleNumberFormatException(Throwable e) {
return "Error, NumberFormatException!";
}
@ExceptionHandler(Throwable.class)
public String handleThrowable(Throwable e) {
return "Error, Throwable!";
}
拦截器
1.在SpringMVC框架中,拦截器是可以运行在所有控制器处理请求之前和之后的一种组件,并且,如果拦截器运行在控制器处理请求之前,还可以选择对当前请求进行阻止或放行
需要注意的是,拦截器的目的并不是"拦截下来后阻止运行",更多的是"拦截下来后执行某些代码",其优势在于可作用于若干种不同请求的处理过程,就是写一个拦截器,就可以在很多种请求的处理过程中被执行
2.只要是若干种不同的请求过程中都需要执行同样的或高度相似的代码,都可以使用拦截器解决,典型的例如验证用户是否已经登录等
3.使用拦截器,需要自定义类,实现HandlerInterceptor接口,实现接口中的三个方法
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("LoginInterceptor.preHandle()");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("LoginInterceptor.postHandle()");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("LoginInterceptor.afterCompletion()");
}
}
4.只有注册了拦截器才会被启用,注册过程通过重写WebMVCConfigure接口中的addInterceptors()方法
@Configuration // 此注解不是必须的
@EnableWebMvc
@ComponentScan("cn.tedu.springmvc") // 必须配置在当前配置类,不可配置在Spring的配置类
public class SpringMvcConfig implements WebMvcConfigurer {
@Override public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/user/login.do");
}
}
preHandle()方法:表示在处理请求之前是阻止还是放行,当这个方法返回的是true时,表示放行,反之就是阻止
afterCompletion()方法:表示在处理请求之后的操作
5.关于注册拦截器时的配置,调用addPathPatter()方法添加哪些路径需要被拦截,这个参数可以是String... ,也可以是List<String>,在编写路径时,还可以使用 * 作为通配符,那么会匹配所有满足/xxx/*/xxx.do
如果需要匹配若干层级,必须使用2个连续的星号:/xxx/**
一旦使用通配符,就有可能导致匹配的范围过大,那么对于我们拦截器设置的目的有出入.
还可以在addInterceptor方法后继续调用excludPathPattern()方法,以添加"排除路径"
小结
1.理解SpringMVC框架的作用:
接收请求,响应结果,处理异常
2.掌握常用注解的使用
3.接收请求参数的方式
将请求参数直接声明在处理请求的方法的参数列表中
将若干个请求参数进行封装,并将封装的类型声明在处理请求的方法的参数列表中
如果是URL中的路径,需要使用@PathVariable
4.掌握响应JSON格式的正文的做法
5.掌握统一响应类型的设计
6.统一处理异常
7.拦截器的创建与配置