1. 程式人生 > >Servlet學習筆記 :網頁應用Blog的迭代

Servlet學習筆記 :網頁應用Blog的迭代

簡介:跟隨林信良老師的《JSP&Servlet學習筆記》,迭代自己的網頁應用Blog。

一、概括

1 - 功能類

  • 登入 :已有賬戶,登入成功顯示歷史動態。
  • 註冊 :沒有賬戶,組冊成功顯示歡迎介面。
  • 釋出 :登入成功,新增動態資訊。
  • 刪除 :登入成功,刪除動態資訊。
  • 登出 :離開應用,取消會話狀態。

2-輔助類

  • 服務中心 :所有功能的抽象模型,在整個應用週期提供服務。
  • 監聽器 :應用初始化時,例項化‘服務中心’至應用上下文(ServletContext)。
  • 過濾器 :攔截未登入使用者的登入性功能操作。

3 - 頁面

  • 登入頁面 :使用者的登入視窗。
  • 註冊頁面 :使用者的註冊視窗。
  • 顯示頁面 :進行動態資訊的展示。
  • 錯誤頁面 :當輸入錯誤的賬號資訊。

二、Blog-Sheep(初次迭代)

1 - 登陸頁面 這裡寫圖片描述

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>BeerForm</title>
    <meta http-equiv="Content-Type" content="text/html;chartset=UTF-8">
</head>
<body
>
<form method="post" action="login.do"> <table> <tr> <td>Username : </td> <td><input type="text" name="username"></td> </tr> <tr> <td>Password : </td
>
<td><input type="text" name="password"></td> </tr> <tr> <td></td> <td> <input type="submit" value="Login"> </td> </tr> </table> <a href="view/register.html"><h6>Register</h6></a> </form> </body> </html>

2 - 註冊頁面 這裡寫圖片描述

<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>
<html>
    <head>
        <meta http-equiv='content-type' content='text/html;charset=UTF-8'>
        <title>Register</title>
    </head>
    <body>
        <form method="post" action="/Blog/register.do">
            <table>
                <tr>
                    <td>Username : </td>
                    <td><input type="text" name="username"></td>
                </tr>
                <tr>
                    <td>Password : </td>
                    <td><input type="text" name="password"></td>
                </tr>
                <tr>
                    <td>Email : </td>
                    <td><input type="text" name="email"></td>
                </tr>
                <tr>
                    <td></td>
                    <td>
                        <input type="submit" value="Register">
                    </td>
                </tr>
            </table>
            <a href="/Blog/index.html"><h6>Login</h6></a>
        </form>
    </body>
</html>

3、DD

<?xml version="1.0" encoding="UTF-8"?>
<web-app version ="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <context-param>
        <param-name>USERS</param-name>
        <param-value>/Users/apple/Documents/RayDB/users</param-value>
    </context-param>

    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <page-encoding>UTF-8</page-encoding>
            <default-content-type>text/html</default-content-type>
        </jsp-property-group>
    </jsp-config>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

</web-app>

4 - 登陸類 這裡寫圖片描述

package com.ray.notebook.init;

import com.ray.notebook.utils.UserService;

import java.io.IOException;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@SuppressWarnings("serial")
@WebServlet("/login.do")
public class Login extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        UserService service = (UserService)getServletContext().getAttribute("service");
        List<String> errors = new ArrayList<String>();

        //Check user input
        if(username.length() == 0)
            errors.add("Please make sure Username no empty!");

        if(password.length() == 0)
            errors.add("Please make sure Password no empty!");

        //Check whether user existed
        if(errors.isEmpty()){
            try{
                if(!service.isInvalidUsername(username))
                    errors.add("Username does not exist.");
                if(!service.checkLogin(username, password))
                    errors.add("Password is not right.");
            }catch(IOException e){
                errors.add("Server is busy, try it again." + e);
            }
        }

        //Dispatch the result
        if(errors.isEmpty()){
            Map<Date, String> messages = new TreeMap<>();
            try{
                messages = service.readMessage(username);
            }catch(IOException e){
                messages.put(new Date(), "Server is busy, refesh to try again." + e);
            }

            //Create session for user who registered success
            HttpSession session = request.getSession();
            session.setAttribute("login", username);
            session.setAttribute("messages", messages);
            request.getRequestDispatcher("/view/Success.jsp").forward(request, response);
        }else{
            request.setAttribute("errors", errors);
            request.getRequestDispatcher("/view/Error.jsp").forward(request, response);
        }
    }

}

5 - 註冊類 這裡寫圖片描述

/*
 * Servlet
 *      1.Handle register.do
 *      2.Create userhome to store user,s data
 */
package com.ray.notebook.init;

import com.ray.notebook.utils.UserService;

import java.io.IOException;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@SuppressWarnings("serial")
@WebServlet("/register.do")
public class Register extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String email = request.getParameter("email");

        UserService service = (UserService)getServletContext().getAttribute("service");
        List<String> errors = new ArrayList<String>();

        //Check user input
        if(username.length() == 0)
            errors.add("Please make sure Username no empty!");

        if(password.length() == 0)
            errors.add("Please make sure Password no empty!");

        if(email.length() == 0)
            errors.add("Please make sure Email no empty!");

        if(service.isInvalidUsername(username))
            errors.add("Username has existed, try another one.");

        //Try to create user data
        if(errors.isEmpty()){
            try{
                service.createUserData(username, password, email);
                service.addMessage(username, "Welcome to Blog.com !");
            }catch(IOException e){
                errors.add("Server is busy, try it again." + e);
            }
        }

        //Dispatch the result
        if(errors.isEmpty()){
            Map<Date, String> messages = new TreeMap<>();
            try{
                messages = service.readMessage(username);
            }catch(IOException e){
                messages.put(new Date(), "Server is busy, refesh to try again." + e);
            }

            //Create session for user who registered success
            HttpSession session = request.getSession();
            session.setAttribute("login", username);
            session.setAttribute("messages", messages);
            request.getRequestDispatcher("/view/Success.jsp").forward(request, response);
        }else{
            request.setAttribute("errors", errors);
            request.getRequestDispatcher("/view/Error.jsp").forward(request, response);
        }
    }

}

6 - 使用者服務中心

package com.ray.notebook.utils;

import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.util.Comparator;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
import java.lang.NullPointerException;

public class UserService{

    /* Directory name where stores users' date */
    private String USERS;

    /* Constructing with a designated directory name */
    public UserService(String USERS){
        this.USERS = USERS;
    }

    /* Constructing with default directory name */
    public UserService(){}

    {
        if(USERS == null)
            USERS = "/Users/apple/Documents/RayDB/users";
    }

    /* Whether the user,s data has been created */
    public boolean isInvalidUsername(String username){
        try{
            for(String file : new File(USERS).list())
            if(file.equals(username))
                return true;
        }catch(NullPointerException e){
            return true;
        }
        return false;
    }

    /* Creating userhome(a directory) to store data */
    public void createUserData(String username, String password, String email) throws IOException{

        File userhome = new File(USERS + "/" + username);
        userhome.mkdir();
        BufferedWriter writer = new BufferedWriter(new FileWriter(userhome + "/profile"));
        writer.write(email + "\t" + password);
        writer.close();
    }

    /* Checking whether the user is valid */
    public boolean checkLogin(String username, String password) throws IOException{

        if(username != null && password != null){
            for(String userhome : new File(USERS).list()){
                if(userhome.equals(username)){
                    BufferedReader reader = new BufferedReader(
                        new FileReader(USERS + "/" + userhome + "/profile"));
                    String pw = reader.readLine().split("\t")[1];
                    reader.close();
                    if(pw.equals(password))
                        return true;
                }
            }
        }
        return false;
    }

    /* Add message into userhome */
    public void addMessage(String username, String data) throws IOException{
        String file = USERS + "/" + username + "/" + new Date().getTime() + ".txt";
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
            new FileOutputStream(file), "UTF-8"));
        writer.write(data);
        writer.close();
    }

    /* Filter : .txt file only */
    private class TxtFileFilter implements FilenameFilter{
        public boolean accept(File file, String name){
            return name.endsWith(".txt");
        }
    }

    /* Comparator : sort java.util.Date by ascending order */
    private class DateComparaor implements Comparator<Date>{
        public int compare(Date a, Date b){
            return -a.compareTo(b);
        }
    }

    /* Return a map which includes all data uder fixed userhome 
     * 
     * Using : file filter which passes .txt only
     *
     * Using : date comparator which sorts dates by ascending order
     *
     */

    private TxtFileFilter filter = new TxtFileFilter();
    private DateComparaor comparator = new DateComparaor();

    public Map<Date, String> readMessage(String username) throws IOException
    {
        File border = new File(USERS + "/" + username);
        String[] txts = border.list(filter);
        Map<Date, String> messages = new TreeMap<Date, String>(comparator);

        for(String txt : txts){
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(USERS + "/" + username + "/" + txt), "UTF-8"));

            String text = null;
            StringBuilder builder = new StringBuilder();
            while((text = reader.readLine()) != null){
                builder.append(text);
            }

            Date date = new Date(
                Long.parseLong(txt.substring(0, txt.indexOf(".txt"))));

            messages.put(date, builder.toString());
            reader.close();
        }

        return messages;
    }

    /* Deleting message in the userhome */
    public void deleteMessage(String username, String message){
        File file = new File(USERS + "/" + username + "/" + message + ".txt");
        if(file.exists())
            file.delete();
    }

}

7 - 監聽器

/*
 * When servlet context was initailized :
 *      1.Accessing param USERS(database) from web.xml;
 *      2.Instantiation class UserService;
 *      3.Setting this instance as an attribute into servlet context.
 */
package com.ray.notebook.listeners;

import com.ray.notebook.utils.UserService;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class DataBaseSCL implements ServletContextListener{
    public void contextInitialized(ServletContextEvent event){
        ServletContext context = event.getServletContext();
        String dataBase = context.getInitParameter("USERS");
        context.setAttribute("service", new UserService(dataBase));
    }

    public void contextDestroyed(ServletContextEvent event){}
}

8 - 過濾器

package com.ray.notebook.filters;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebFilter("/secure/*")
public class UserFilter implements Filter{
    public void doFilter(ServletRequest request, ServletResponse response, 
        FilterChain chain) throws ServletException, IOException{
        HttpServletRequest req = (HttpServletRequest) request;
        if(req.getSession().getAttribute("login") != null){
            chain.doFilter(request, response);
        }else{
            HttpServletResponse resp = (HttpServletResponse) response;
            resp.sendRedirect("/Blog/index.html");
        }
    }

    public void destroy(){}
}

9 - 新增動態 這裡寫圖片描述

package com.ray.notebook.secure;

import com.ray.notebook.utils.UserService;

import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/secure/add.do")
public class AddBlog extends HttpServlet{
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException{
        request.setCharacterEncoding("UTF-8");

        HttpSession session = request.getSession();
        String username = (String)session.getAttribute("login");

        //Try to add blog into userhome
        UserService service = (UserService) getServletContext().getAttribute("service");
        String blog = request.getParameter("blog");
        if(blog.length() != 0)
            service.addMessage(username, blog);

        //Return messages to user
        Map<Date, String> messages = new TreeMap<>();
        messages = service.readMessage(username);
        session.setAttribute("messages", messages);
        request.getRequestDispatcher("/view/Success.jsp").forward(request, response);
    }
}

10 - 刪除動態 這裡寫圖片描述

11 - 登出

package com.ray.notebook.secure;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/secure/logout.do")
public class LogOut extends HttpServlet{
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException{

        HttpSession session = request.getSession();
        session.invalidate();
        response.sendRedirect("/Blog/index.html");
    }
}

12 - 動態頁面

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>
<html>
    <head>
        <meta http-equiv='content-type' content='text/html;charset=UTF-8'>
        <title>Success</title>
    </head>
    <body>

        Hello,${sessionScope.login}:<br>
        <form method="post" action="/Blog/secure/add.do">
            <textarea clos="60" rows="4" name="blog"></textarea>
            <br><button type="submit">Submit</button>
        </form>

        <br><h3>History Message:</h3>
        <c:forEach var="entry" items="${sessionScope.messages}">
            <br>${entry.value}
            <br><fmt:formatDate value="${entry.key}" type="both" dateStyle="full" timeStyle="full"/>
            <br><a href="/Blog/secure/delete.do?blog=${entry.key.time}">Delete</a>
        </c:forEach>

        <br><a href="/Blog/secure/logout.do">Logout</a>
    </body>
</html>

13 - 錯誤頁面

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>
<html>
    <head>
        <meta http-equiv='content-type' content='text/html;charset=UTF-8'>
        <title>Error</title>
    </head>
    <body>
        <c:forEach var="error" items="${errors}">
            <li>${error}</li>
        </c:forEach>

        <a href="view/register.html"><h6>Register</h6></a>
    </body>
</html>

14 - JAR包支援 這裡寫圖片描述

三、Blog-Snake(二次迭代)

  • 初次迭代時,所有的資訊以單獨的字串形式出現。
  • 二次迭代時,對使用者的資訊進行封裝。
  • 使用者的註冊資訊封裝成使用者類(使用者名稱、密碼、郵箱)。
  • 動態資訊封裝成動態類(使用者名稱、日期、內容)。
  • 利用資訊封裝類對UserService類進行重構。
  • 優點是資訊的傳遞更清晰,方法引數更簡單。
  • 缺點是原來有些方法只需要傳遞單個字串,現在卻必須傳遞整個物件。
  • 可以採用方法過載,避免原來使用UserService類的程式碼修改。
  • 筆者是初學者,重寫了所有類。

1 - 將每個使用者的註冊資訊封裝成類

package com.ray.notebook.utils;

public class User{
    private String username;
    private String password;
    private String email;

    public User(){}

    public User(String username, String password, String email){
        this.username = username;
        this.password = password;
        this.email = email;
    }

    public void setUsername(String username){
        this.username = username;
    }

    public String getUsername(){
        return username;
    }

    public void setPassword(String password){
        this.password = password;
    }

    public String getPassword(){
        return password;
    }

    public void setEmail(String email){
        this.email = email;
    }

    public String getEmail(){
        return email;
    }
}

2 - 將每條動態封裝成類

package com.ray.notebook.utils;

import java.util.Date;

public class Blog{
    private String username;
    private Date date;
    private String text;

    public Blog(){}

    public Blog(String username, Date date, String text){
        this.username = username;
        this.date = date;
        this.text = text;
    }

    public void setUsername(String username){
        this. username = username;
    }

    public String getUsername(){
        return username;
    }

    public void setDate(Date date){
        this. date = date;
    }

    public Date getDate(){
        return date;
    }

    public void setText(String text){
        this. text = text;
    }

    public String getText(){
        return text;
    }
}

3 - 重構使用者中心

package com.ray.notebook.utils;

import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
//import java.util.Map;
//import java.util.TreeMap;
import java.util.List;
import java.util.ArrayList;
import java.lang.NullPointerException;

public class UserService{

    /* Directory name where stores users' date */
    private String USERS;

    /* Constructing with a designated directory name */
    public UserService(String USERS){
        this.USERS = USERS;
    }

    /* Constructing with default directory name */
    public UserService(){}

    {
        if(USERS == null)
            USERS = "/Users/apple/Documents/RayDB";
    }

    /* Whether the user,s data has been created */
    // public boolean isInvalidUsername(String username){
    //  try{
    //      for(String file : new File(USERS).list())
    //      if(file.equals(username))
    //          return true;
    //  }catch(NullPointerException e){
    //      return true;
    //  }
    //  return false;
    // }

    /* Refractoring : Using user class as a whole */
    public boolean isInvalidUsername(User user){
        try{
            for(String file : new File(USERS).list()){
                if(file.equals(user.getUsername()))
                    return true;
            }
        }catch(NullPointerException e){
            return true;
        }
        return false;
    }

    /* Creating userhome(a directory) to store data */
    // public void createUserData(String username, String password, String email) throws IOException{

    //  File userhome = new File(USERS + "/" + username);
    //  userhome.mkdir();
    //  BufferedWriter writer = new BufferedWriter(new FileWriter(userhome + "/profile"));
    //  writer.write(email + "\t" + password);
    //  writer.close();
    // }

    /* Refractoring : Using user class as a whole */
    public void createUserData(User user) throws IOException{

        File userhome = new File(USERS + "/" + user.getUsername());
        userhome.mkdir();
        BufferedWriter writer = new BufferedWriter(new FileWriter(userhome + "/profile"));
        writer.write(user.getEmail() + "\t" + user.getPassword());
        writer.close();
    }

    /* Checking whether the user is valid */
    // public boolean checkLogin(String username, String password) throws IOException{

    //  if(username != null && password != null){
    //      for(String userhome : new File(USERS).list()){
    //          if(userhome.equals(username)){
    //              BufferedReader reader = new BufferedReader(
    //                  new FileReader(USERS + "/" + userhome + "/profile"));
    //              String pw = reader.readLine().split("\t")[1];
    //              reader.close();
    //              if(pw.equals(password))
    //                  return true;
    //          }
    //      }
    //  }