<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.19.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.springboot</groupId> <artifactId>webdev2</artifactId> <version>0.0.1-SNAPSHOT</version> <name>webdev2</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version> <!--佈局功能支持程序,thymeleaf 使用 3.0 版本以上時支持程序要使用 2.0 以上--> <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>4.3.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
html, body { height: 100%; } body { display: -ms-flexbox; display: -webkit-box; display: flex; -ms-flex-align: center; -ms-flex-pack: center; -webkit-box-align: center; align-items: center; -webkit-box-pack: center; justify-content: center; padding-top: 40px; padding-bottom: 40px; background-color: #f5f5f5; } .form-signin { width: 100%; max-width: 330px; padding: 15px; margin: 0 auto; } .form-signin .checkbox { font-weight: 400; } .form-signin .form-control { position: relative; box-sizing: border-box; height: auto; padding: 10px; font-size: 16px; } .form-signin .form-control:focus { z-index: 2; } .form-signin input[type="email"] { margin-bottom: -1px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .form-signin input[type="password"] { margin-bottom: 10px; border-top-left-radius: 0; border-top-right-radius: 0; }
package com.springboot.webdev2.bean; import java.util.Date; public class User { public User() { } public User(Integer id,String name, String email, Integer gender, Date birthday) { this.id = id; this.name = name; this.email = email; this.gender = gender; this.birthday = birthday; } private Integer id; private String name; private String email; private Integer gender; private Date birthday; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this.gender = gender; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
package com.springboot.webdev2.dao; import com.springboot.webdev2.bean.User; import org.springframework.stereotype.Repository; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @Repository public class UserDao { private static List<User> userList; private static int id = 3; static { /** * 模擬 db 數據 */ userList = new ArrayList<>(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); try { userList.add(new User(1,"張三", "asda@qq.com", 1, simpleDateFormat.parse("2019-1-1"))); userList.add(new User(2,"李四", "32432@qq.com", 0, simpleDateFormat.parse("2014-3-11"))); userList.add(new User(3,"王五", "dae@qq.com", 1, simpleDateFormat.parse("2012-1-1"))); } catch (ParseException e) { e.printStackTrace(); } } public List<User> listAll(){ return userList; } public void save(User user){ user.setId(++id); userList.add(user); } public void delete(Integer id){ userList.removeIf(p -> p.getId() == id); } public User getById(Integer id){ for (User user : userList) { if (user.getId() == id) { return user; } } return null; } public void update(User user){ User dbUser = getById(user.getId()); dbUser.setName(user.getName()); dbUser.setBirthday(user.getBirthday()); dbUser.setEmail(user.getEmail()); dbUser.setGender(user.getGender()); } }
以前咱們經過 Maven 引入的依賴 jar 中大多都是類文件,其實經過 Maven 還能夠引入靜態文件,好比咱們下面要用的 BootStrap,咱們只須要添加以下依賴:css
<dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>4.3.1</version> </dependency>
若是要引入其它靜態資源依賴可參考 webjars.org。html
原理:經過以前的學習咱們已經瞭解了靜態資源映射,只要將靜態資源放在 classpath:/META-INF/resources/webjars/ 下,咱們就能夠經過 /webjars/** 請求訪問到對應靜態資源。java
下面以登陸頁面作國際化爲示例。jquery
一、首先要建立國際化使用的資源文件:web
login.password=密碼 login.please=請登陸 login.rememberMe=記住我 login.signin=登陸 login.username=用戶名
login.password=password login.please=please login login.rememberMe=Remember Me login.signin=Sing In login.username=UserName
二、配置國際化資源目錄:spring
# 若是有多個目錄能夠 , 分隔,起始目錄爲 classpath spring.messages.basename=i18n.login
三、編寫登陸模板頁面,引用 BootStrap,使用 #{屬性名} 引入國際化資源屬性:apache
<!doctype html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>[[#{login.please}]]</title> <link th:href="@{/webjars/bootstrap/4.3.1/css/bootstrap.css}" rel="stylesheet"> <link th:href="@{/css/signin.css}" rel="stylesheet"> </head> <body class="text-center"> <form class="form-signin" th:action="@{/login}" method="post"> <h1 class="h3 mb-3 font-weight-normal">[[#{login.please}]]</h1> <label for="inputEmail" th:text="#{login.username}" class="sr-only"></label> <input type="text" id="inputEmail" th:name="username" class="form-control" th:placeholder="#{login.username}" required autofocus> <label for="inputPassword" th:text="#{login.password}" class="sr-only"></label> <input type="password" id="inputPassword" th:name="password" class="form-control" th:placeholder="#{login.password}" required> <div class="checkbox mb-3"> <label> <input type="checkbox" value="remember-me">[[#{login.rememberMe}]] </label> </div> <button class="btn btn-lg btn-primary btn-block" th:text="#{login.signin}" type="submit"></button> <span th:if="${msg!=null}" style="color:red;">[[${msg}]]</span> <p class="mt-5 mb-3 text-muted">© 2019-2020</p> </form> </body> </html>
四、作完上述步驟其實已經完成了登陸頁面國際化,但 SpringBoot 默認配置的國際化解析器是根據客戶端請求頭來返回對應語言資源的。若是咱們想要定製本身的國際化規則,能夠自定義國際化解析器以下:bootstrap
package com.springboot.webdev2.component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.LocaleResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Locale; /** * 自定義國際化解析器 * 做用: * 若是客戶端請求攜帶了語言參數,則根據語言參數返回相應資源 * 若是客戶端未攜帶語言參數,則根據語言請求頭返回相應資源 */ public class MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest httpServletRequest) { // 獲取請求語言參數 String langCode = httpServletRequest.getParameter("langCode"); Locale locale = Locale.getDefault(); // 若是沒有傳遞請求參數 if (StringUtils.isEmpty(langCode)) { // 獲取語言請求頭 String languageHeader = httpServletRequest.getHeader("Accept-Language"); String[] langInfo = languageHeader.split(";"); if (langInfo != null && langInfo.length != 0) { String langsStr = langInfo[0]; String[] langArr = langsStr.split(","); langCode = langArr[0]; } } String[] strs = langCode.split("-"); locale = new Locale(strs[0], strs[1]); return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } }
五、接下來咱們就能夠配置頁面映射,還須要注意的是,編寫完國際化解析器後還須要將其註冊到容器中才會生效:springboot
package com.springboot.webdev2.config; import com.springboot.webdev2.component.MyLocaleResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * SpringMVC 擴展配置類 */ @Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { @Bean public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){ WebMvcConfigurerAdapter webMvcConfigurerAdapter = new WebMvcConfigurerAdapter(){ @Override public void addViewControllers(ViewControllerRegistry registry) { // 配置首頁映射爲登陸頁 registry.addViewController("/").setViewName("login"); registry.addViewController("/index.html").setViewName("login"); } }; return webMvcConfigurerAdapter; } /** * 註冊國際化解析器到容器 */ @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); } }
六、測試:session
不指定語言參數,請求 localhost:8080:
指定語言參數,請求 localhost:8080?langCode=en-US
Spring Boot 自動配置好了國際化資源文件的組件,對應類爲:
1 @Configuration 2 @ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT) 3 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) 4 @Conditional(ResourceBundleCondition.class) 5 @EnableConfigurationProperties 6 // 與 spring.messages 下屬性綁定 7 @ConfigurationProperties(prefix = " spring.messages") 8 public class MessageSourceAutoConfiguration { 9 10 private static final Resource[] NO_RESOURCES = {}; 11 12 /* 13 國際化資源文件的默認基礎名爲 messages 14 因此咱們國際化屬性能夠直接定義在 classpath 下 messages.properties 文件中 15 */ 16 private String basename = "messages"; 17 18 private Charset encoding = Charset.forName("UTF-8"); 19 20 private int cacheSeconds = -1; 21 22 private boolean fallbackToSystemLocale = true; 23 24 private boolean alwaysUseMessageFormat = false; 25 26 @Bean 27 public MessageSource messageSource() { 28 ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 29 if (StringUtils.hasText(this.basename)) { 30 // 設置國際化資源文件的基礎名(如:login_zh_CN、login_en_US 的基礎名就爲 login) 31 messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray( 32 StringUtils.trimAllWhitespace(this.basename))); 33 } 34 if (this.encoding != null) { 35 messageSource.setDefaultEncoding(this.encoding.name()); 36 } 37 messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale); 38 messageSource.setCacheSeconds(this.cacheSeconds); 39 messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat); 40 return messageSource; 41 } 42 43 public String getBasename() { 44 return this.basename; 45 } 46 47 public void setBasename(String basename) { 48 this.basename = basename; 49 } 50 51 public Charset getEncoding() { 52 return this.encoding; 53 } 54 55 public void setEncoding(Charset encoding) { 56 this.encoding = encoding; 57 } 58 59 public int getCacheSeconds() { 60 return this.cacheSeconds; 61 } 62 63 public void setCacheSeconds(int cacheSeconds) { 64 this.cacheSeconds = cacheSeconds; 65 } 66 67 public boolean isFallbackToSystemLocale() { 68 return this.fallbackToSystemLocale; 69 } 70 71 public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) { 72 this.fallbackToSystemLocale = fallbackToSystemLocale; 73 } 74 75 public boolean isAlwaysUseMessageFormat() { 76 return this.alwaysUseMessageFormat; 77 } 78 79 public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) { 80 this.alwaysUseMessageFormat = alwaysUseMessageFormat; 81 } 82 83 }
1 @Bean 2 @ConditionalOnMissingBean // 當容器中沒有國際化解析器時才使用註冊以下解析器,即若是咱們本身在容器中註冊了國際化解析器 SpringMVC 就會使用咱們本身的國際化解析器 3 @ConditionalOnProperty(prefix = "spring.mvc", name = "locale") 4 public LocaleResolver localeResolver() { 5 if (this.mvcProperties 6 .getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) { 7 return new FixedLocaleResolver(this.mvcProperties.getLocale()); 8 } 9 AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); 10 localeResolver.setDefaultLocale(this.mvcProperties.getLocale()); 11 return localeResolver; 12 }
一、頂部欄:
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0"> <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="#">[[${#session.getAttribute('loginUser')}]]</a> <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search"> <ul class="navbar-nav px-3"> <li class="nav-item text-nowrap"> <a class="nav-link" href="#">Sign out</a> </li> </ul> </nav>
二、抽取左邊菜單欄,動態高亮:
<nav class="col-md-2 d-none d-md-block bg-light sidebar" xmlns:th="http://www.thymeleaf.org"> <div class="sidebar-sticky"> <ul class="nav flex-column" th:with="activeURI=${#request.getRequestURI()}"> <li class="nav-item"> <a th:class="${activeURI}==@{/} or ${acitveURI}==@{/index.html} ?'nav-link active':'nav-link'" th:href="@{/}"> <span data-feather="home"></span> 首頁 </a> </li> <li class="nav-item"> <a th:class="${activeURI}==@{/users}?'nav-link active':'nav-link'" th:href="@{/users}"> <span data-feather="file"></span> 用戶列表 </a> </li> </ul> </div> </nav> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script>window.jQuery || document.write('<script src="../../../../assets/js/vendor/jquery-slim.min.js"><\/script>')</script> <!-- Icons --> <script src="https://unpkg.com/feather-icons/dist/feather.min.js"></script> <script> feather.replace() </script>
三、首頁引用公共頁:
<!doctype html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>首頁</title> <link th:href="@{/webjars/bootstrap/4.3.1/css/bootstrap.css}" rel="stylesheet"> <link th:href="@{/css/index.css}" rel="stylesheet"> </head> <body> <nav th:replace="fragment/topbar :: nav"/> <div class="container-fluid"> <div class="row"> <nav th:replace="fragment/leftmenu"/> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2>首頁</h2> </main> </div> </div> </body> </html>
一、編寫登陸控制器:
package com.springboot.webdev2.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import javax.servlet.http.HttpSession; @Controller public class LoginController { @GetMapping("/login") public String loginView(){ return "login"; } @PostMapping("/login") public String login(String username, String password, HttpSession session, Model model){ if(!StringUtils.isEmpty(username)&&"123456".equals(password)){ session.setAttribute("loginUser",username); return "redirect:/index.html"; } model.addAttribute("msg", "用戶名或密碼錯誤"); return "/login"; } }
二、編寫登陸攔截器:
package com.springboot.webdev2.component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { Object loginUser = httpServletRequest.getSession().getAttribute("loginUser"); if (loginUser==null){ httpServletResponse.sendRedirect("/login"); return false; } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
三、註冊攔截器:
package com.springboot.webdev2.config; import com.springboot.webdev2.component.LoginInterceptor; import com.springboot.webdev2.component.MyLocaleResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * SpringMVC 擴展配置類 */ @Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter { @Bean public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){ WebMvcConfigurerAdapter webMvcConfigurerAdapter = new WebMvcConfigurerAdapter(){ @Override public void addInterceptors(InterceptorRegistry registry) { // 註冊攔截器,不攔截 /login 請求 registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/login"); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/index.html").setViewName("index"); } }; return webMvcConfigurerAdapter; } /** * 註冊國際化解析器到容器 */ @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); } }
一、編寫 User 控制器:
package com.springboot.webdev2.controller; import com.springboot.webdev2.bean.User; import com.springboot.webdev2.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.List; @Controller public class UserController { @Autowired private UserDao userDao; @GetMapping("/users") public String listAll(Model model) { List<User> users = userDao.listAll(); model.addAttribute("userList", users); return "users"; } @GetMapping("/user/{id}") public String updateView(@PathVariable Integer id, Model model) { User user = userDao.getById(id); model.addAttribute("user", user); return "user"; } @GetMapping("/user") public String addView(Model model) { // 由於頁面使用了 th:object ,若是 user 爲 null 會異常 model.addAttribute("user", new User()); return "user"; } @PostMapping("/user") public String save(User user) { userDao.save(user); return "redirect:/users"; } @PutMapping("/user") public String update(User user) { userDao.update(user); return "redirect:/users"; } @DeleteMapping("/user/{id}") public String delete(@PathVariable Integer id) { userDao.delete(id); return "redirect:/users"; } }
二、編寫列表頁:
<!doctype html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>用戶列表</title> <link th:href="@{/webjars/bootstrap/4.3.1/css/bootstrap.css}" rel="stylesheet"> <link th:href="@{/css/index.css}" rel="stylesheet"> </head> <body> <nav th:replace="fragment/topbar :: nav"/> <div class="container-fluid"> <div class="row"> <nav th:replace="fragment/leftmenu"/> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2>用戶列表</h2> <button th:onclick="|window.location.href='@{/user}'|" class="btn btn-sm btn-success">添加</button> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <th>編號</th> <th>姓名</th> <th>郵箱</th> <th>性別</th> <th>生日</th> <th>操做</th> </tr> </thead> <tbody> <tr th:each="user : ${userList}"> <td th:text="${user.id}"/> <td th:text="${user.name}"/> <td th:text="${user.email}"/> <td th:text="${user.gender}==0?'女':'男'"/> <td th:text="${#dates.format(user.birthday,'yyyy-MM-dd')}"/> <td> <form th:action="@{/user/}+${user.id}" method="post"> <input type="button" th:onclick="|window.location.href='@{/}user/${user.id}'|" class="btn btn-sm btn-info" value="編輯"/> <input type="hidden" name="_method" value="delete"> <input type="submit" value="刪除" class="btn btn-sm btn-danger"/> </form> </td> </tr> </tbody> </table> </div> </main> </div> </div> </body> </html>
三、編寫編輯頁:
<!doctype html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>[[${user.id==null?'添加':'編輯'}]]</title> <link th:href="@{/webjars/bootstrap/4.3.1/css/bootstrap.css}" rel="stylesheet"> <link th:href="@{/css/index.css}" rel="stylesheet"> </head> <body> <nav th:replace="fragment/topbar :: nav"/> <div class="container-fluid"> <div class="row"> <nav th:replace="fragment/leftmenu"/> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2>用戶[[${user.id==null?'添加':'編輯'}]]</h2> <form th:action="@{/user}" method="post" th:object="${user}"> <input type="hidden" name="id" th:value="*{id}"> <input type="hidden" name="_method" value="put" th:unless="${user.id==null}"> <div class="form-group"> <lable>姓名:</lable> <input type="text" name="name" th:value="*{name}" class="form-control" placeholder="姓名"/> </div> <div class="form-group"> <lable>郵箱:</lable> <input type="email" name="email" th:value="*{email}" class="form-control" placeholder="郵箱"/> </div> <div class="form-group"> <lable>性別:</lable> <br> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" th:checked="*{gender}==1" 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" th:checked="*{gender}==0" name="gender" value="0"/> <label class="form-check-label">女</label> </div> </div> <div class="form-group"> <label>生日:</label> <input type="text" name="birthday" th:value="*{#dates.format(birthday,'yyyy-MM-dd')}" class="form-control" placeholder="生日"/> </div> <button class="btn btn-lg btn-primary btn-block" th:text="提交" type="submit"></button> <span th:if="${msg!=null}" style="color:red;">[[${msg}]]</span> <p class="mt-5 mb-3 text-muted">© 2019-2020</p> </form> </main> </div> </div> </body> </html>
四、效果:
實現了 Restful 風格的 CRUD
列表頁:
新增:
編輯:
1 @Bean 2 @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) 3 public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { 4 return new OrderedHiddenHttpMethodFilter(); 5 }