文章目录
rest风格接口
-
restfurl:描述性状态转移。用来做接口。
-
通过url来定义资源,通过method(get、post、put、delete)来描述动作。
-
涉及到的两个注解
-
@RestController(不常用),定义接口,表示本类中所有的方法都是异步,不用写ResponseBody,不用写ResponseEntity
-
@PathValiable:注解处理器方法的形参,用来获取url中变量值。
-
-
注意点:在put与delete请求中,请求参数如果是key=value&key=value,后端收不到数据?
-
tomcat默认只对get和post请求的key=value&key=value解析,保存内部的map,我们可以通过getParameter来获取数据。
-
解决方法:在web.xml配置过滤器FormContentFilter,对put和delete请求支持。
-
@Controller
@RequestMapping("rest")
public class RestUrlHandler {
@GetMapping("{sid}") // 查询 http://localhost:8080/0803/rest/12
@ResponseBody
public ResponseBean getStuById(@PathVariable("sid") String sid){
SysStudent stu = new SysStudent();
stu.setSid(sid);
stu.setSname("千珏");
stu.setSpass("1234");
return new ResponseBean(StatusEnum.SUCCESS,stu);
}
@PostMapping // 新增 http://localhost:8080/0803/rest
public ResponseEntity addStu(@RequestBody SysStudent sysStudent){
System.out.println(sysStudent);
return ResponseEntity.ok(new StatusBean(StatusEnum.SUCCESS));
}
@PutMapping // 修改 http://localhost:8080/0803/rest
public ResponseEntity editStu(@RequestBody SysStudent sysStudent){
System.out.println(sysStudent);
return ResponseEntity.ok(new StatusBean(StatusEnum.SUCCESS));
}
@DeleteMapping("{sid}") // 删除 http://localhost:8080/0803/rest/12
public ResponseEntity deleteStuById(@PathVariable("sid") String sid){
System.out.println(sid);
return ResponseEntity.ok(new StatusBean(StatusEnum.SUCCESS));
}
}
SpringMVC异常处理
- 原生servlet玩法:在web.xml配置错误代码,错误页面;
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
-
局部异常处理:对某个处理器类中的异常处理。通过@ExceptionHandler注解
- @ExceptionHandler可以转页面,可以返回json数据。
- 缺点:只能对一个类中的异常生效。
@Controller
@RequestMapping("exception")
public class ExceptionTestHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public Map<String,Object> doException(Exception e){
Map<String,Object> map = new HashMap<>();
map.put("time",System.currentTimeMillis());
map.put("info",e.getMessage());
return map;
}
@DeleteMapping("{sid}") // 删除
public ResponseEntity deleteStuById(@PathVariable("sid") Integer sid){
// int i = 1/0; // 算术异常
if (sid == 12){
throw new RuntimeException(StatusEnum.ERROR.getMsg());
}
System.out.println(sid);
return ResponseEntity.ok(new StatusBean(StatusEnum.SUCCESS));
}
}
-
全局异常处理:对所有的处理器类中的异常处理。实现HandlerExceptionResolver接口。
-
针对的同步请求,返回错误页面,可以自定义错误信息。
-
缺点:对于前后端分离项目,不能返回数据。
-
@Component
public class GlobExceptionHandler implements HandlerExceptionResolver {
//返回ModelAndView对象.处理同步请求。
@Override
public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse resp, Object o, Exception e) {
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
mv.addObject("time",System.currentTimeMillis());
mv.addObject("info",e.getMessage());
HandlerMethod hm = (HandlerMethod) o;
Method method = hm.getMethod();
String name = method.getDeclaringClass().getName();
mv.addObject("clzName",name);
return mv;
}
}
-
全局统一异常处理:通过@ControllerAdvice与@ExceptionHandler两者结合。
-
一个状态枚举类,两个响应实体类(StatusBean,ResponseBean)
-
正常返回,返回ResponseBean
-
异常情况,返回StatusBean,全部进入一个统一的全局异常处理类。
-
@Component // 注入容器
@ControllerAdvice // controller增强注解。做异常的统一处理。
public class GlobExceptionHandler2 {
@ExceptionHandler(Exception.class)
public ResponseEntity doException(Exception e){
return ResponseEntity.ok(new StatusBean(StatusEnum.OPS_ERROR));
}
@ExceptionHandler(MyException.class)
public ResponseEntity doException(MyException e){
return ResponseEntity.ok(new StatusBean(e.getStatusEnum()));
}
}
静态资源处理
- 静态资源包括:css、js、jpg、png、音频、视频、html等。
- 解决方式1:
<!--所有的静态资源都走DefaultServlet,依赖应用服务器名字叫default的serlvet-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
- 解决方式2:
<!--对静态资源做映射,映射到一个springmvc提供的公共的静态资源处理器-->
<mvc:resources location="/static/" mapping="/static/**"></mvc:resources>
<mvc:resources location="/error/" mapping="/error/**"></mvc:resources>
文件上传下载
-
SpringMVC对apache的commons-fileupload组件进行封装,避免了繁琐的request解析操作。
-
操作流程:
- 导入jar包:fileUpload、io
- 配置bean
<!-- id:必须是multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="50000000"></property><!-- 最大文件大小 --> <property name="maxInMemorySize" value="10000000"></property><!-- 临时文件域 --> <property name="defaultEncoding" value="UTF-8"></property><!-- 中文文件名 --> <property name="uploadTempDir" value="/upload/tmp"></property><!-- 临时文件存储路径 --> </bean>
- 在handler类中添加upload()方法,参数列表添加MultipartFile类型形参接收上传的文件。
-
多文件上传,upload方法MultipartFile形参改为数组,或多个MultipartFile类型的形参。
@Controller
@RequestMapping("file")
public class FileHandler {
// 文件上传
@PostMapping("upload")
public ResponseEntity doUpload(MultipartFile file, HttpServletRequest req) throws IOException {
if (file == null) {
System.out.println("无文件");
return ResponseEntity.ok("无文件");
}
InputStream inputStream = file.getInputStream(); // 大文件上传
byte[] bytes = file.getBytes();// 适合小文件,10M以下
String filename = file.getOriginalFilename(); // 文件真实名
long size = file.getSize();// 文件大小
// 上传文件至服务器
String realPath = req.getServletContext().getRealPath("/");
String savePath = "/upload/" + filename;
System.out.println(realPath + savePath);
File target = new File(realPath + savePath);
FileUtils.writeByteArrayToFile(target, bytes); // 保存文件
// 把文件的保存路径返回给客户端
return ResponseEntity.ok(new ResponseBean(StatusEnum.SUCCESS, savePath)); // /upload/dui.png
}
//前端如何调用下载接口:<a href="/download?filepath=/upload/aaa.png"> <button click="location.href=/download?filepath=xxxxx"></button>
@GetMapping("/download")
public ResponseEntity doDownLoad(String filepath, HttpServletRequest req) throws IOException {
System.out.println("文件下载!");
// /upload/dui.png
String realPath = req.getServletContext().getRealPath(filepath);
File file = new File(realPath);
String name = file.getName();
byte[] bytes = FileUtils.readFileToByteArray(file);
if (bytes != null) {
//指定响应头,传字节数组
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(name, "UTF-8"));
return new ResponseEntity(bytes, headers, HttpStatus.OK);
} else {
throw new NullPointerException(StatusEnum.ERROR.getMsg());
}
}
}
拦截器
-
interceptor拦截器,是控制层框架中的一个概念,与Filter概念一样。
-
url请求,处理器映射器找到一个处理器方法对象以及一系列的拦截器对象(preHandle,postHandle,afterCompletion)。在执行处理器方法的前,后,异常处执行拦截器对应的方法。
6.1 从HandlerInterceptor派生拦截器类。
6.2 配置拦截器(拦截范围,忽略范围)
<mvc:interceptors>
<mvc:interceptor>
<!--配置拦截路径-->
<mvc:mapping path="/**"/>
<!--忽略路径-->
<mvc:exclude-mapping path="/user5/login"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/static/**"></mvc:exclude-mapping>
<!--拦截器bean对象-->
<bean class="com.javasm.sys.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Object login_user = session.getAttribute("LOGIN_USER");
if(login_user!=null){
return true;
}else{
//未登陆,要做什么.
// 如果是同步请求,可以转登陆页面
// 如果是异步请求,则返回数据。
throw new MyException(StatusEnum.NO_LOGIN);
}
}
// @Override
// public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// //处理器方法执行完毕,并正常返回,执行这里
// System.out.println("后拦截");
// }
//
// @Override
// public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// //处理器方法执行完毕,不论有没有异常,都执行最终拦截
// System.out.println("最终拦截");
// }
}
跨域请求
- 跨域是指浏览器不允许当前页面的所在的源去请求另一个源的数据。源指协议,端口,域名。只要这个3个中有一个不同就是跨域。
方法1:@CrossOrigin注解
- 该注解添加到在处理器方法或类上。
- 缺点:只能够针对单个类处理。
方法2:springMVC的全局配置
<mvc:cors>
<mvc:mapping path="/**"
allowed-origins="*"
allowed-headers="*"
allowed-methods="*"
allow-credentials="true"/>
</mvc:cors>
// 前端axios需要开启跨域支持
axios.defaults.baseURL = 'http://localhost:8080';
axios.defaults.withCredentials=true;//跨域支持
- 缺点:方法1与方法2在正常情况下都能够正常处理跨域请求,一旦项目中引入拦截器,则跨域处理失效,因为springmvc的cors配置也是基于拦截器实现的,并且默认作为最后一个拦截器处理,这样会导致在跨域拦截器之前执行了业务拦截器。比如:引入登录拦截器,A请求被登录拦截,则不向下执行跨域拦截器处理,该请求则不支持跨域,客户端收到错误信息。(springMVC高版本貌似已解决此问题)
方法3:CrosFilter过滤器
<!-- 这个配置需要放到Spring的配置文件中,不能放到SpringMVC的配置文件,因为SpringMVC的加载是基于Servlet,它是晚于Filter的 -->
<bean id="corsFilter" class="org.springframework.web.filter.CorsFilter">
<constructor-arg name="configSource">
<bean class="org.springframework.web.cors.UrlBasedCorsConfigurationSource">
<property name="corsConfigurations">
<map>
<entry key="/**">
<bean class="org.springframework.web.cors.CorsConfiguration">
<property name="allowCredentials" value="true"/>
<property name="allowedMethods">
<list>
<value>GET</value>
<value>POST</value>
<value>HEAD</value>
<value>PUT</value>
<value>Delete</value>
<value>OPTIONS</value>
</list>
</property>
<property name="allowedHeaders" value="*"/>
<property name="allowedOrigins" value="*"/>
</bean>
</entry>
</map>
</property>
</bean>
</constructor-arg>
</bean>
<!-- web.xml
由于CorsFilter跟通常的Filter不一样,Spring对其做了很多改造,所以加载的方式要使用DelegatingFilterProxy,通过Spring的方式把它放到容器中
-->
<filter>
<filter-name>myCorsFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>corsFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myCorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
方法4:自定义跨域处理过滤器(推荐)
public class MyCrosFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 预检请求,请求头Origin是客户端地址,要求跨域头不能是*
String origin = httpRequest.getHeader("Origin");
if (origin == null) {
httpResponse.addHeader("Access-Control-Allow-Origin", "*");
} else {
httpResponse.addHeader("Access-Control-Allow-Origin", origin);
}
httpResponse.addHeader("Access-Control-Allow-Headers",
"Origin, x-requested-with, Content-Type, Accept,X-Cookie,token");
//httpResponse.addHeader("Access-Control-Expose-Headers", "token");
httpResponse.addHeader("Access-Control-Allow-Credentials", "true");
httpResponse.addHeader("Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS,DELETE");
//预检请求,直接通过。
if (httpRequest.getMethod().equals("OPTIONS")) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
} catch (Exception e) {
throw e;
}
}
@Override
public void destroy() {
}
}
- web.xml配置
<!--跨域拦截器的配置-->
<filter>
<filter-name>myCrosFilter</filter-name>
<filter-class>com.javasm.common.filter.MyCrosFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myCrosFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
方法5:springboot的解决方式
@Configuration
public class MyConfiguration {
//springmvc中 没有FilterRegistrationBean这个类
@Bean
public FilterRegistrationBean这个类 corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}