2.5 完整的程序

在前面的章节中我们学会了构建一个最简单的Blade程序,这节来做一个相对完整的较为简单的Web程序。

这个程序只有一个登陆的功能,用户名和密码暂时写在配置文件中。

这样的一个程序大概可以在脑海里浮现,嗯。。有一个登陆页面,一个提交表单的请求,一个登陆成功后的页面。
还需要一个注销的功能,非常简单的web常用小功能。

我们梳理一下完成这个功能的思路:

  1. 编写配置文件将用户名密码存储起来
  2. 创建4个路由(index, login[GET], login[POST], logout)
  3. 编写login.html, index.html 页面
  4. 编写登陆逻辑,将登陆成功状态存储在session中
  5. 编写注销逻辑,将登陆状态从session清除
  6. 编写一个webhook,用于拦截没有登陆的用户

简单明了,佩服我自己 23333

第一步,加载配置

项目还是继续用之前创建的 first-blade-app,在 resources 目录下创建一个 app.properties 的配置文件,Blade会在启动的时候加载它,然后我们加入两行配置:

  1. app.username=hello@gmail.com
  2. app.password=blade123

配置加好了,怎么读取呢,头大!!!哈哈,在blade中读取配置的方式非常简单,因为 app.properties 会在启动的时候加载,我们可以利用 Blade 的事件机制,当启动完毕后我们读取一下配置就可以了,怎么做呢?

  1. Blade.me().event(EventType.SERVER_STARTED, (e) -> {
  2. Environment environment = e.blade.environment();
  3. Const.USERNAME = environment.get("app.username").get();
  4. Const.PASSWORD = environment.get("app.password").get();
  5. }).start(Application.class);

这段代码是配置一个启动后执行的事件,将配置文件中的 usernamepassword 存储到常量中去。

第二步,创建路由

虽然项目非常的小,你可以不用创建包,直接写在主类里或者写一个 Controller,为了让大家养成良好的编码习惯,我还是将控制器写在 controller 子包中。

  1. @Path
  2. public class AuthController {
  3. @GetRoute("index")
  4. public String index() {
  5. return "index.html";
  6. }
  7. @GetRoute("login")
  8. public String login() {
  9. return "login.html";
  10. }
  11. @PostRoute("login")
  12. public String doLogin(User user, Request request, Response response) {
  13. if (StringKit.isBlank(user.getUsername())) {
  14. request.attribute("error", "用户名不能为空");
  15. return "index.html";
  16. }
  17. if (StringKit.isBlank(user.getPassword())) {
  18. request.attribute("error", "用户名不能为空");
  19. return "login.html";
  20. }
  21. if (!Const.USERNAME.equalsIgnoreCase(user.getUsername()) ||
  22. !Const.PASSWORD.equalsIgnoreCase(user.getPassword())) {
  23. request.attribute("error", "用户名或密码错误");
  24. return "login.html";
  25. }
  26. request.session().attribute(Const.LOGIN_SESSION_KEY, user.getUsername());
  27. response.redirect("/index");
  28. return null;
  29. }
  30. @GetRoute("logout")
  31. public void logout(Session session, Response response) {
  32. session.removeAttribute(Const.LOGIN_SESSION_KEY);
  33. response.redirect("/login");
  34. }
  35. }

代码逻辑也非常简单,登陆成功后将用户名存储在session里,注销后跳转到登陆页面;Blade支持将 usernamepassword 通过对象的方式传递到后台中,你也可以选择一个参数一个参数的获取。

第三步,编写页面

resources 目录下创建一个名为 templates 的目录,我们将模板文件放在这里

login.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <title>登陆页面</title>
  8. <!-- Bootstrap core CSS -->
  9. <link href="/webjars/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  10. <!-- Custom styles for this template -->
  11. <link href="/static/css/signin.css" rel="stylesheet">
  12. <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
  13. <!--[if lt IE 9]>
  14. <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
  15. <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
  16. <![endif]-->
  17. </head>
  18. <body>
  19. <div class="container">
  20. <form class="form-signin" method="post" action="/login">
  21. <h2 class="form-signin-heading">来吧客官 :)</h2>
  22. <label for="inputEmail" class="sr-only">邮箱</label>
  23. <input type="email" name="username" class="form-control" placeholder="请输入登陆邮箱" required autofocus>
  24. <label class="sr-only">Password</label>
  25. <input type="password" name="password" class="form-control" placeholder="请输入密码" required/>
  26. <p style="color: red">${error}</p>
  27. <button class="btn btn-lg btn-primary btn-block" type="submit">登 陆</button>
  28. </form>
  29. </div>
  30. </body>
  31. </html>

刚好复习一下前面学习的 webjars 将bootstrap的css引入进来,登陆页面是这个样子:

2.5 完整的程序 - 图1

然后编写一个 index.html 页面

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登陆成功</title>
  6. </head>
  7. <body>
  8. <h1>${loginuser} 大兄弟,你获得力量了!!</h1>
  9. <a href="/logout">我要注销</a>
  10. </body>
  11. </html>

第四步,拦截未登录

我们不允许未登录的用户访问 index,那么在blade中最简单的实现方式就是注册一个 before 请求或者实现 webhook,这里我们简单起见写一个 before 请求完成这个功能,逻辑复杂的时候请使用webhook单独去处理。

  1. Blade.me().before("/*", ((request, response) -> {
  2. String uri = request.uri();
  3. if("/index".equals(uri)){
  4. String username = request.session().attribute(Const.LOGIN_SESSION_KEY);
  5. if (StringKit.isBlank(username)) {
  6. response.redirect("/login");
  7. return;
  8. }
  9. }
  10. }));

判断当访问 /index 的时候session中是否有登陆状态,如果没有则跳转到登陆页面。

  1. public class Const {
  2. public static String USERNAME;
  3. public static String PASSWORD;
  4. public static final String LOGIN_SESSION_KEY = "loginuser";
  5. }
  6. public class User {
  7. private String username;
  8. private String password;
  9. public String getUsername() {
  10. return username;
  11. }
  12. public void setUsername(String username) {
  13. this.username = username;
  14. }
  15. public String getPassword() {
  16. return password;
  17. }
  18. public void setPassword(String password) {
  19. this.password = password;
  20. }
  21. }

客官,登陆吧

当我们输入错误的用户名密码,页面会有提示,输入正确后会是这样:

2.5 完整的程序 - 图2

点击 我要注销 后会跳转到登陆页面,本章节的源码在 github