这篇文章准备来记录一下一个restful风格小项目的流程,上篇文章为它做了一个基础,如果有什么错误希望大家能够指出。
目录
- 首页
- 国际化
- 登录
- 拦截器
- CRUD
一、首页
- 在访问localhost:8080/的时候,默认访问首页
- 在自己配置的SpringMVC的配置类中
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Bean//将这个组件注册到容器中public WebMvcConfigurer webMvcConfigurer(){WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");registry.addViewController("/login.html").setViewName("login");}};return webMvcConfigurer;}
}
二、国际化
- 步骤
- 编写国际化配置文件,抽取页面需要显示的国际化消息
目录结构
login.properties代表默认的;下面的两个分别代表英文和中文的
在编写页面需要的国际化消息时,可以点击任意的一个properties文件,然后切换到Resource Bundle视图,然后使用+进行每个消息的编写
- 使用ResourceBundleMessageSource管理国际化资源文件
如果是在SpringMVC中使用的时候,需要我们自己去编写;但是在SpringBoot中是给自动配置好了的
我们在编写的时候是在resourses目录下的i18n文件夹下写了配置文件,但是在观察源码发现他表示配置文件是放在类路径下的message.properties中,所以我们需要在application.propertis中去修改一下
- 去页面获取国际化的值
效果:根据浏览器语言设置的信息切换了国际化;
2. 用按钮去实现切换国际化
- 原理
国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象);
- 步骤
在页面上添加超链接
编写自定义的LocaleResolver
public class MyLocalResolver implements LocaleResolver {@Overridepublic Locale resolveLocale(HttpServletRequest httpServletRequest) {String l = httpServletRequest.getParameter("l");//使用默认的Locale locale = Locale.getDefault();if(!StringUtils.isEmpty(l)){String[] s = l.split("_");locale = new Locale("s[0]","s[1]");}return locale;}@Overridepublic void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {}
}
在MyMvcConfig中将我们自己编写的LocaleResolver添加到容器中;
@Bean
public LocaleResolver localeResolver(){return new MyLocalResolver();
}
三、登录
1.不连接数据库,只要用户名不为空和密码等于123456就可以成功登录并且跳转到首页
@Controller
public class LoginController {//可以使用Restful风格的注解// @RequestMapping(value = "/user/login",method = RequestMethod.POST)@PostMapping(value="/user/login")public String login(@RequestParam("username") String username,@RequestParam("password") String password, Map<String,Object> map){if(!StringUtils.isEmpty(username) && "123456".equals(password)){//登录成功,为了防止表单重复提交,将重定向到dashboardreturn "redirect:/main.html";}else{//登录失败map.put("msg","用户名密码错误");}return "login";}
}
2. 开发期间模板引擎页面修改以后,为了保证他能实时生效,需要执行以下步骤:
- 禁用模板引擎的缓存
#禁用模板引擎的缓存
spring.thymeleaf.cache=false
- idea中用Ctrl+f9:重新编译
3. 登录错误信息的显示
<p th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
4. 为了防止表单重复提交,将重定向进首页,所以在自己的MvcConfig配置类中再添加一个视图渲染;但是我们在设置了这个之后,会发现直接在地址栏中输入跳到首页的网址是可以直接进去的,那么我们的登录功能就不是摆设了吗?所以需要使用拦截器
registry.addViewController("/main.html").setViewName("dashboard");return "redirect:/main.html";
四、拦截器
1.自定义拦截器
/*** 这个拦截器的作用就是:进行登录的检查,如果不通过的话是不能进行主页的访问和一些增删改查功能的*/
public class LoginHandlerInterceptor implements HandlerInterceptor {//目标方法执行之前@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Object loginUser = request.getSession().getAttribute("loginUser");if (loginUser==null){//在页面显示错误信息request.setAttribute("msg","没有权限");//未登录,返回登录页面request.getRequestDispatcher("/login.html").forward(request,response);return false;}else{//已登录return true;}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
2. 将拦截器注入到容器中
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Bean//将这个组件注册到容器中public WebMvcConfigurer webMvcConfigurer(){WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {//注册拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {//由于SpringBoot已经做好了静态资源的映射,拦截器不会拦截静态资源//addPathPatterns()表示会对那种类型的映射进行拦截,"/**"表示/全部拦截//excludePathPatterns()表示排除一些不拦截的registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/login.html","/","/user/login");}@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");registry.addViewController("/login.html").setViewName("login");registry.addViewController("/main.html").setViewName("dashboard");}};return webMvcConfigurer;}
3. 在Controller中是利用了session来进行用户名的存储,然后在拦截器中判断用户名是否为空
@Controller
public class LoginController {//可以使用Restful风格的注解// @RequestMapping(value = "/user/login",method = RequestMethod.POST)@PostMapping(value="/user/login")public String login(@RequestParam("username") String username,@RequestParam("password") String password,Map<String,Object> map,HttpSession session){if(!StringUtils.isEmpty(username) && "123456".equals(password)){session.setAttribute("loginUser",username);//登录成功,为了防止表单重复提交,将重定向到dashboardreturn "redirect:/main.html";}else{//登录失败map.put("msg","用户名密码错误");}return "login";}
}
五、CRUD
1.员工列表
- 实验要求
CRUD满足rest风格;
URI:/资源名称、资源标识;
HTTP请求方式区分对资源CRUD操作;
- Controller代码实现
@Controller
public class EmployeeController {@AutowiredEmployeeDao employeeDao;//获取所有员工@GetMapping("/emps")public String list(Model model){//获取所有员工Collection<Employee> all = employeeDao.getAll();//将获取到的员工存进session域中model.addAttribute("emps",all); //thymeleaf默认就会进行拼串;前缀是classpath:/templates/xxxx.htmlreturn "emp/list";}
}
- Thymeleaf公共页面元素抽取
三种引入功能片段的th属性:
th:insert----将公共片段整个插入声明引入元素中;
th:replace---将声明引入的元素替换为公共片段;
th:include----将被引入的片段的内容包含进这个标签中;
- 链接高亮
thymeleaf实现只有在点击侧边栏某个按钮时,只有点击的按钮高亮;将之前的侧边栏和顶部栏代码抽取出来放在一个新的HTML文件里
侧边栏---dashboard
<a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#"th:class="${activeUri=='main.html'}?'nav-link active':'nav-link '">
侧边栏----员工管理<a class="nav-link active" href="#"th:href="@{/emps}" th:class="${activeUri=='emps'?'nav-link active':'nav-link '}">
dashboard.html(将activeUri的值传递到引用的侧边栏里,进行判断)
<div th:replace="commens/bar::#sidebar(activeUri='main.html')"></div>
list.html
<div th:replace="commens/bar::#sidebar(activeUri='emps')"></div>
- 员工列表显示
<thead><tr><th>#</th><th>lastName</th><th>gender</th><th>department</th><th>birth</th><th>操作</th></tr>
</thead>
<tbody><tr th:each="emp:${emps}"><td th:text="${emp.lastName}"></td><td th:text="${emp.lastName}"></td><td th:text="${emp.gender}==0?'男':'女'"></td><td th:text="${emp.department.departmentName}"></td><td th:text="${#dates.format(emp.birth,'yyyy--MM-dd:HH:mm')}"></td><td><button class="btn btn-sm btn-primary">修改</button><button class="btn btn-sm btn-danger">删除</button></td></tr>
2.添加员工
- Controller
@GetMapping("/addEmp")
public String toAddPage(Model model){Collection<Department> departments = departmentDao.getDepartments();model.addAttribute("depts",departments);//返回到添加页面return "emp/add";
}
@PostMapping("/addEmp")public String addEmp(Employee employee){System.out.println("保存的员工是:"+employee);employeeDao.save(employee);//来到员工列表页面//redirect:重定向到一个地址//forward:转发到一个地址return "redirect:/emps";}
- 前台页面
跳转请求
<a href="addEmp" class="btn btn-sm btn-success">添加</a>
add.html
<form th:action="addEmp" method="post"><div class="form‐group"><label>LastName</label><input name="lastName" type="text" class="form‐control" placeholder="zhangsan"></div><div class="form‐group"><label>Email</label><input name="email" type="email" class="form‐control" placeholder="zhangsan@atguigu.com"></div><div class="form‐group"><label>Gender</label><br/><div class="form‐check form‐check‐inline"><input class="form‐check‐input" type="radio" name="gender" value="1"><label class="form‐check‐label">男</label></div><div class="form‐check form‐check‐inline"><input class="form‐check‐input" type="radio" name="gender" value="0"><label class="form‐check‐label">女</label></div></div><div class="form‐group"><label>department</label><select class="form‐control"name="department.id"><option th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}">1</option></select></div><div class="form‐group"><label>Birth</label><input name="birth" type="text" class="form‐control" placeholder="zhangsan"></div><button type="submit" class="btn btn‐primary">添加</button>
</form>
- 使用添加功能主要会出现的错误是400错误,前台输入的格式不匹配导致
我们这个功能这边主要是日期的格式会导致错误
填写的日期格式主要有:2017/01/01 2017-01-01 2017.01.01
日期的格式话:SpringMVC需要将页面提取的值转化为指定的类型;
默认的日期格式是按照/的格式
可以在application.properties配置文件中修改
spring.mvc.date-format=yyyy-MM-dd
3.修改员工
- 在做员工修改的时候发现修改页面和新增页面是差不多的,只需要通过判断去判定执行的是添加功能还是修改功能
add.html
<!--对功能的判定就是通过emp是否为空,不为空则是修改-->
<form th:action="@{/addEmp}" method="post"><input type="hidden" name="_method" value="put" th:if="${emp!=null}"/><!--在传递到Controller的时候也要把id传过去(修改功能的时候)--><input type="hidden" th:if="${emp!=null}" th:value="${emp.id}"/><div class="form‐group"><label>LastName</label><input th:value="${emp!=null}?${emp.lastName}" name="lastName" type="text" class="form‐control" placeholder="zhangsan"></div><div class="form‐group"><label>Email</label><input th:value="${emp!=null}?${emp.email}" name="email" type="email" class="form‐control" placeholder="zhangsan@atguigu.com"></div><div class="form‐group"><label>Gender</label><br/><div class="form‐check form‐check‐inline"><input class="form‐check‐input" type="radio" name="gender" value="1"th:checked="${emp!=null}?${emp.gender==1}"><label class="form‐check‐label">男</label></div><div class="form‐check form‐check‐inline"><input class="form‐check‐input" type="radio" name="gender" value="0"th:checked="${emp!=null}?${emp.gender==0}"><label class="form‐check‐label">女</label></div></div><div class="form‐group"><label>department</label><select class="form‐control"name="department.id"><option th:selected="${emp!=null}?${dept.id==emp.department.id}" th:value="${dept.id}" th:each="dept:${depts}" th:text="${dept.departmentName}"></option></select></div><div class="form‐group"><label>Birth</label><input th:value="${emp!=null}?${emp.birth}" name="birth" type="text" class="form‐control" placeholder="zhangsan"></div><button type="submit" class="btn btn‐primary"th:text="${emp!=null}?'修改':'添加'"></button>
</form>
- Controller
//来到修改页面,查出当前员工,并进行回显
@GetMapping("/editEmp/{id}")
public String toEditPage( @PathVariable("id") Integer id, Model model){Employee employee = employeeDao.get(id);model.addAttribute("emp",employee);//页面显示所有的部门信息Collection<Department> departments = departmentDao.getDepartments();model.addAttribute("departments",departments);return "emp/add";
}
//进行员工修改,需要传递该员工的id
@PutMapping("/addEmp")
public String updateEmp(Employee employee){System.out.println("收到的员工信息:"+employee);System.out.println(employeeDao);employeeDao.save(employee);return "redirect:/emps";
}
- 这里重点提一下在写这段代码会出现的一个错误:如果使用的是较高版本的SpringBoot,在修改的时候使用的是put请求,SpringBoot是为我们配置好了hiddenFilter,但是他默认是不开启的,需要在application.properties中进行开启
#开启过滤器
spring.mvc.hiddenmethod.filter.enabled=true@Bean
@ConditionalOnMissingBean({FormContentFilter.class})
@ConditionalOnProperty(prefix = "spring.mvc.formcontent.filter",name = {"enabled"},matchIfMissing = true
)
public OrderedFormContentFilter formContentFilter() {return new OrderedFormContentFilter();
}
4.删除员工
- Controller
//员工删除方法
@DeleteMapping("/deleteEmp/{id}")
public String deleteEmp(@PathVariable("id") Integer id){employeeDao.delete(id);return "redirect:/emps";
}
- 前台页面最简单的编写就是将删除按钮用form表单包起来,然后发送delete请求,再传递id;但是这样的页面就不太美观,所以可以使用js点击事件来进行编写
<form th:action="@{/deleteEmp/}+${emp.id}" method="post"><input type="hidden" name="_method" value="delete"><button type="submit" class="btn btn-sm btn-danger">删除</button>
</form>
点击事件:
<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn‐sm btn‐danger deleteBtn">删除</button></td> </tr><script>$(".deleteBtn").click(function(){//删除当前员工的$("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();return false; });
</script>
这个小实验到这里就写的差不多了,和Spring的框架差不多,只是在写的过程中可以知道一些技巧和thymeleaf的标签的使用,这里只贴了一部分代码,完整代码可以进入我的GitHub:https://github.com/rainbow-dan/SpringBoot-rest-crud;如果你喜欢我的文章,就关注我吧!