先创建一个Controller包,在里面写一些网页的跳转:
@Controller
注解标注类的方法,return时会被视图处理器识别成静态文件的路径。
因此先在Controller包中新建的类上标注@Controller
。@ResponseBody
为返还json格式的数据。
注意:需加上页面渲染工具 Thymeleaf,否则不能跳转页面。
1、网站登录页
//当访问 / 或者 /login时跳转到首页
@GetMapping(value ={"/","/login"})
public String loginPage(){
return "login";
} //return的login为login.html文件
// 只要放在template中,去掉前和后缀只写需要跳转的html网页即可
2、登录后的页面
当在首页时,表单提交则进入登录后的页面(这里是简单测试,不管输入什么都会登录)
还有一点,如果登陆后一直刷新网页就相当于一直在重复提交,因此要把登陆后的状态识别开。这里使用了重定向。
//当首页以 post提交到/login时,跳转到以下页面 因为接收方式和上面的get不一样所以可以一样为login
@PostMapping("/login")
public String main(User user, HttpSession session){
//登录成功重定向到index.html
if (!"".equals(user.getUsername())&&!"".equals(user.getPassword())) {
//不为空,将用户的值传到下一个跳转的页面
session.setAttribute("user",user);
//登录成功重定向跳转到index页面
return "redirect:index.html";
}
else
//如果什么都没输入直接登录,则回到登录页(相当于一直在登录页)
return "login";
}
3、防止在任何情况直接访问到index.html网页
如果一开始就直接访问index.html页面,由于用户没有登录,所以是为空的,直接跳转到登录页面,如果用户不为空,则继续留在index页面(防止了重复提交)
/**
* 去index页面
* @return
*/
//防止重复提交 造成一直登录 使用以上的重定向
@GetMapping("/index.html")
public String PreventRepetitionSubmit(HttpSession session, Model model){
User user= (User) session.getAttribute("user");
if (null!=user) {
session.setAttribute("user",user);
return "index";
}
else {
model.addAttribute("msg","请登录");
return "login";
}
}
Thymeleaf
这里面使用了Thymeleaf插件,th:text="${msg}
代表的是后台Model中的msg值;
th:action="@{/login}
相当于将form表单中的属性值给他改了;
以上两个都是写在标签体内部的,标签体外的话使用;
[[${session.user.username}]]
注意!!!:因为我们的模板是放在template,用return访问这些html文件时一定要在
html文件的抬头加入<html xmlns:th="http://www.thymeleaf.org">
,这样就能不加template和.html后缀直接访问里面的html文件,但是如果在template下面的文件夹中的html要加上这个文件夹的名字。类似table/dynamic_table
这样。
还可以通过thymeleaf管理网页的公共部分,例如:
先创建一个common.html网页, 他们的这一部分是公共的,可以在引入以上的抬头后,在公共的地方加上th:fragment="commonheader"
,然后把公共的部分复制到这个里面去,要用的时候就直接在需要被用到的地方加上 <div th:include="common :: commonheader"></div>
就可以了,include是包含在里面,replcae是代替原来的,insert是插入进去
如果是以id而不是<head th:fragment="commonheader">
这样写的话,用<div th:replcae="common :: #leftmenu"></div>
这样去表示。注意变化了#号
<head th:fragment="commonheader">
<meta charset="UTF-8">
<title>Title</title>
<!--common-->
<link href="css/style.css" th:href="@{/css/style.css}" rel="stylesheet">
<link href="css/style-responsive.css" th:href="@{/css/style-responsive.css}" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="js/html5shiv.js" th:src="@{/js/html5shiv.js}"></script>
<script src="js/respond.min.js" th:src="@{/js/respond.min.js}"></script>
<![endif]-->
</head>
在其他需要的地方输入以下这个引入
<div th:insert="common :: #leftmenu"></div>
使用Thymeleaf,遍历(相当于)数据库中的信息,呈现到前端网页表格中(简单示例):
前端的html代码,user为封装的实体类,里面有username和password。
th:each="user,stats:${users}
相当于在遍历后台传的数据,stats为传过来的所有状态信息,有index和count可以看出有多少个,相当于编号。
<tr class="gradeX" th:each="user,stats:${users}">
<td th:text="${stats.count}"></td>
<td th:text="${user.username}"></td>
<td>[[${user.password}]]</td>
</tr>
后端java代码,是通过Model放进去的,相当于request.setAttribute:
@GetMapping("/dynamic_table")
public String dynamic_table(Model model){
List<User> list= Arrays.asList(new User("syf","123"),new User("wfl","456"),
new User("wgx","471"),new User("mhy","qwe"));
model.addAttribute("users",list);
return "table/dynamic_table";
}
拦截器
先创建一个自己的拦截器类,实现HandlerInterceptor接口,重写它的三个方法,
/**
* 拦截器:
* 做登录检查
* 1、配置好拦截器要拦截哪些请求
* 2、把这些配置放在容器
* 3、
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登录检查逻辑
log.info("拦截的请求路径"+request.getRequestURI());//打印拦截请求日志
HttpSession session=request.getSession();
Object loginUser=session.getAttribute("user");
if (null!=loginUser) {
//放行
return true;
}
else {
//不放行 未登录,跳转到登录界面
request.setAttribute("msg","请登录");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
}
/**
* 目标方法完成以后
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 页面渲染以后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
然后将自己写的拦截器添加进去,添加到标注了@Configuration
标签的类里面
/**
* 1、编写的拦截器实现HandlerInterceptor接口
* 2、拦截器注册到容器中,通过实现WebMvcConfigurer的addInterceptors
* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
/*
添加新创建的拦截器,addPathPatterns要拦截的,excludePathPatterns要放行的
*/
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")//会把静态资源也拦截了
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**");//把静态资源下的这几个放行
}
}
文件上传
先将底层写死的能够上传的文件大小修改,在application.properties中
#单个文件的最大上传大小
spring.servlet.multipart.max-file-size=1000MB
#整个上传的文件大小、
spring.servlet.multipart.max-request-size=10000MB
Html中提交文件的表单form写法:
<form class="form-horizontal" role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
文件上传表单的固定写法
如果是上传多个文件,在input中<input type="file" name="lifephoto" id="life" multiple>
多了个multiple
表单提交过来的的数据处理:
其中form_layouts.html为文件表单上传所在的html文件名,MultipartFile 为上传的文件的数据类型,@RequestPart("headimage")
用法和RequestParam一样,都是把表单中上传文件的name填进去即可。
@Slf4j
@Controller
public class FileUpload {
@GetMapping("/form_layouts")
public String uploadTest(){
return "form/form_layouts.html";
}
//MultipartFile会自动封装上传过来的文件
@PostMapping("/upload")
public String upload(@RequestParam("username")String username,
@RequestParam("email")String email,
@RequestPart("headimage") MultipartFile headimage,//单文件
@RequestPart("lifephoto") MultipartFile[] lifephoto) throws IOException {//多文件,文件太大就不行了需要修改
log.info("上传的信息 ,email={},username={},headimage={},lifephoto={}",
email,username,headimage.getSize(),lifephoto.length);
if (!headimage.isEmpty()) {
String original=headimage.getOriginalFilename();
headimage.transferTo(new File("C:\\Users\\syf\\Desktop\\test\\"+original));
}
if (lifephoto.length>0) {
for (MultipartFile x:lifephoto)
if (!x.isEmpty()){
x.transferTo(new File("C:\\Users\\syf\\Desktop\\test\\"+x.getOriginalFilename()));
}
}
return "index";
}
}
异常错误处理
可以在templates下创建error文件夹,将404.html和5xx.html放进去,这样我们就可以自定义错误界面了,还可以用Thymeleaf显示错误信息,也可以将404写成4xx。
1、自定义的异常处理器
这个能处理数学运算异常和空指针异常
/**
* 处理整个 web Controller异常
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 代表当前为异常处理器
* 下面的标注value中的内容代表处理数学异常和空指针异常
*/
@ExceptionHandler({ArithmeticException.class,NullPointerException.class})
public String handlerMathException(Exception e){
log.info("异常是:"+e);
return "login";
}
}
2、自定义的异常
/**
* 自定义的异常
* 返回状态码信息的标注
* HttpStatus.FORBIDDEN 拒绝403
* reason为错误的原因
*/
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "用户量太多")
public class UserTooManyException extends RuntimeException{
public UserTooManyException(String message){
super(message);
}
public UserTooManyException() {
}
}
3、自定义的异常解析器
我们把优先级设置为最高相当于什么错误都是它处理
/**
* 自定义的异常解析器
*/
@Order(value = PriorityOrdered.HIGHEST_PRECEDENCE)//异常处理的优先级,数字越小优先级越大
@Component
public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) {
try {
httpServletResponse.sendError(522,"522错误");
} catch (IOException ioException) {
ioException.printStackTrace();
}
return new ModelAndView();
}
}