2016年3月3日 星期四

HelloServlet (Servlet 一)

java EE文件可參考這篇
Tomcat和Servlet的版本對應和API可參考這篇
還有一個我覺得比較有用的spec在,目前只看到3.0的,點進去後,選3.0,然後Maintenance Release的旁邊有個Download page,進去後還有個Download按鈕,再進去選接受,然後下載
另外還有高手翻譯好的版本在這裡

安裝好Tomcat後,Eclipse如下操作:


1.


2.Dynamic web module version為Servlet版本


3.保持預設即可


4.Context root在這裡可以改,預設和專案名稱一樣,最下面的web.xml最好是勾起來


5.完成圖,TestServlet是專案名稱,如果有勾web.xml就會出現在最下面,src為寫java的地方


6.在專案名稱按右鍵-->Properties,如下選擇後,可以修改Context root


7A-1.在src按右鍵New-->Class後,如下設定(也可以New-->Servlet用畫面設定,在下面的7B)


7A-2.完成後是個空的class,覆寫兩個方法


7A-3.覆寫doGet和doPost


7B-1.紅框的部分和7A-1一樣,綠框為預設的,一般都是繼承這個類別



7B-2.Description可打可不打,URL mappings預設是和Class name一樣,可以修改,也可以新增,像我新增了一個/ooo



7B-3.紅框的建構子大部分都用不到,Inherited abstract methods必須勾起來,下面的12個方法才能勾,看要覆寫哪個就勾哪個,上面的Interfaces是說,如果還有要implements的,可以在這Add

※Finish後,3.0之前(不含3.0)的會在web.xml加剛剛的資訊;3.0之後(含)會變成annotation


8.兩個方法修改如下:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("-----Request-----");
    System.out.println("Method=" + req.getMethod());
    System.out.println("CharacterEncoding1=" + req.getCharacterEncoding());
    System.out.println("ContentType=" + req.getContentType());
    req.setCharacterEncoding("UTF-8");
    System.out.println("CharacterEncoding2=" + req.getCharacterEncoding());
    
    System.out.println("RequestURL=" + req.getRequestURL());
    System.out.println("Scheme=" + req.getScheme());
    System.out.println("ServerName=" + req.getServerName());
    System.out.println("ServerPort=" + req.getServerPort());
    System.out.println("ContextPath=" + req.getContextPath());
    System.out.println("ServletPath=" + req.getServletPath());
    System.out.println("pathInfo=" + req.getPathInfo());
    System.out.println("RequestURI=" + req.getRequestURI());
    
    System.out.println("QueryString=" + req.getQueryString());
    System.out.println("\r\nParameterNames Start");
    Enumeration<String> name = req.getParameterNames();
    while (name.hasMoreElements()) {
        String n = name.nextElement();
        System.out.println(n);
        System.out.println(req.getParameter(n) + "\r\n");
    }
    System.out.println("ParameterNames End");
    
    String[] sArray = req.getParameterValues("xxx");
    if (sArray != null) {
        for (String s : sArray) {
            System.out.println(s);
        }
    }
    
    System.out.println("-----Response-----");
    System.out.println("Status=" + resp.getStatus());
    System.out.println("CharacterEncoding1=" + resp.getCharacterEncoding());
    // resp.setCharacterEncoding("Big5");
    System.out.println("設定contentType前=" + resp.getContentType());
    resp.setContentType("text/html;charset=Big5");
    System.out.println("設定contentType後=" + resp.getContentType());
    System.out.println("CharacterEncoding2=" + resp.getCharacterEncoding());
    
    PrintWriter out = resp.getWriter();
    out.println("<html>");
    out.println("<head>");
    out.println("<title>first Servlet</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("Hello 第一支 Servlet!");
    out.println("</body>");
    out.println("</html>");
}
    
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req, resp);
}

※System.out.println會出現在eclipse的Console

※PrintWriter的println會印在網頁上




9.在WebContext按右鍵New-->JSP File-->檔名叫login.jsp-->Finish,內容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        <form action="xxx" method="get">
            <input type="text" name="username" value="user" /><br />
            <input type="password" name="password" value="pass" /><br />
            <input type="radio" name="ooo" value="aaa" />apple<br />
            <input type="radio" name="ooo" value="bbb" />banana<br />
            <input type="radio" name="ooo" value="ppp" />pineapple<br />
            <input type="checkbox" name="xxx" value="ttt" />tiger<br />
            <input type="checkbox" name="xxx" value="rrr" />rabit<br />
            <input type="checkbox" name="xxx" value="lll" />lion<br />
            <input type="hidden" name="hid" value="中文" /><br />
            <input type="text" name="reon" readonly="readonly" value="read" /><br />
            <input type="text" name="died" disabled="disabled" value="disable" /><br />
            <input type="reset" /><br />
            <input type="submit" />
        </form>
    </body>
</html>

※先不要在WEB-INF下創建,它是個特殊的資料夾,使用者進不去的資料夾



10.Web.xml
<welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
</welcome-file-list>

※這是預設的welcome-file,表示伺服器啟動完畢後會run第一個網頁,沒有就run第二個,以此類推,都沒有就是404
開啟server後,網址是http://localhost:8080/TestServletXXX/,不管有沒有對應到,都是這個,沒有對應到就是404,對應到畫面會變,但網址不變,而我的jsp叫login.jsp,所以對應不到可以在後面加上login.jsp也是ok,只是差在網址不一樣

※在第5張圖點web.xml或Deployment Descriptor:TestServlet就可以開啟web.xml,因為web.xml又叫佈署描述檔

※在tomcat資料夾下的webapps\examples\WEB-INF\web.xml可以複製



<display-name>TestServlet</display-name>
<welcome-file-list>
    <welcome-file>login.jsp</welcome-file>
</welcome-file-list>
    
<servlet>
    <servlet-name>ooo</servlet-name>
    <servlet-class>controller.HelloServlet</servlet-class>
</servlet>
    
<servlet-mapping>
    <servlet-name>ooo</servlet-name>
    <url-pattern>/xxx</url-pattern>
</servlet-mapping>

※預設的welcome-file我都沒有,所以修改成一行

※當瀏覽器傳xxx進來時就會找到ooo,然後會去找servlet的servlet-name是ooo的servlet-class就連到後端了

※結果:
-----Request-----
Method=GET
CharacterEncoding1=null
ContentType=null
CharacterEncoding2=UTF-8
RequestURL=http://localhost:8080/TestServletXXX/xxx
Scheme=http
ServerName=localhost
ServerPort=8080
ContextPath=/TestServletXXX
ServletPath=/xxx
pathInfo=null
RequestURI=/TestServletXXX/xxx
QueryString=username=user&password=pass&hid=%E4%B8%AD%E6%96%87&reon=read

ParameterNames Start
username
user

password
pass

hid
中文

reon
read

ParameterNames End
-----Response-----
Status=200
CharacterEncoding1=ISO-8859-1
設定contentType前=null
設定contentType後=text/html;charset=Big5
CharacterEncoding2=Big5

※如果改成post,結果如下:
-----Request-----
Method=POST
CharacterEncoding1=null
ContentType=application/x-www-form-urlencoded
CharacterEncoding2=UTF-8
RequestURL=http://localhost:8080/TestServletXXX/xxx
Scheme=http
ServerName=localhost
ServerPort=8080
ContextPath=/TestServletXXX
ServletPath=/xxx
pathInfo=null
RequestURI=/TestServletXXX/xxx
QueryString=null

...以下和Get一樣

※中文會變成%xx,等同java.net.URLEncoder.encode("中文", "UTF-8"),相反的是
java.net.URLDecoder.decode("%E4%B8%AD%E6%96%87", "UTF-8")

※pathInfo目前是null,它顯示的是jsp的action,xxx對應servlet的路徑,它會顯示後面的路徑,
假設action="xxx/ooo/aaa",那麼pathInfo就是/ooo/aaa,但現在是完全對應,所以目前這樣打會404,後面會再說

※所以RequestURI是ContextPath+ServletPath+pathInfo

※QueryString是get時就是網址後面?後面的部分(不包括?),而post就是null

※注意事項

1.req.getParameterValues是針對checkbox用的,用req.getParameterNames抓出每一個name時,只會抓到第1個
補充:還有getHeader(String)、getHeaders(String)(這個把它想像成getHeaderValues會比較好,官網命名的太爛)、getHeaderNames

2.jsp的action上面沒有「/」開頭
「xxx」是http://localhost:8080/TestServletXXX/xxx
「/xxx」是http://localhost:8080/xxx
也就是少了ContextPath

3.Servlet 3的新功能可以不用web.xml,用annotation的方式也可以,但兩者是可以並存的,所以第4張圖最好是勾起來,annotation如下設定就好了
@WebServlet(urlPatterns = { "/xxx" })
public class HelloServlet extends HttpServlet {
// ...

也就是多個@WebServlet就搞定了,name可以不打

4.前端disabled的是沒有辦法傳到後端的,有經驗的應該都知道

5.Servlet Reloading:也就是改完程式後,不用重起server就會抓到新的程式碼的設定(要注意Console有跑才是有抓到)
Tomcat官網的左邊
左邊Documentation的Tomcat x.0
左邊Reference的Configuration
左邊Containers的Context
Common Attributes有個reloadable,預設是false
可以寫在context.xml(tomcat6以上(含)才有)的<Context>裡或者server.xml的<Host>裡面有可以放<Context>
我自己沒有在<Host>寫,但不知哪時被tomcat加上去了,如下:
<Context docBase="TestServlet" path="/TestServletXXX" reloadable="true" source="org.eclipse.jst.jee.server:TestServlet" />
但這是針對一個專案設定

6.看Servlet API
javax.servlet.Servlet是介面

javax.servlet.GenericServlet是抽象類別,實作了Servlet,較重要的方法有init()、destroy() service(ServletRequest, ServletResponse)

javax.servlet.http.HttpServlet是抽象類別,繼承了GenericServlet
重要方法:
  doGet(HttpServletRequest, HttpServletResponse) 瀏覽器、link、表單
  doPost(HttpServletRequest, HttpServletResponse) 表單
  doOptions(HttpServletRequest, HttpServletResponse):詢問server有哪些方法,也就是doXXX,這個沒那麼重要
  service(ServletRequest, ServletResponse):這是覆寫的
  service(HttpServletRequest req, HttpServletResponse resp):這是重載
當瀏覽器進來覆寫的service會收到,然後給重載的service,然後判斷是doXXX,最後將http資訊傳給它

javax.servlet.ServletRequest、javax.servlet.ServletResponse是介面

javax.servlet.http.HttpServletRequest、javax.servlet.http.HttpServletResponse是介面: resp有404、500...等狀態碼

servlet container(tomcat)實作了介面,所以可以直接用它的方法

繼承圖
所以一般都是繼承HttpServlet並覆寫doGet和doPost,測試時也有人繼承GenericServlet並覆寫service方法




※url-pattern 匹配規則
1.「/」開頭,「/*」結尾
2.副檔名匹配: *.xxx
3.空,只剩 url-pattern 標籤:context 不會有 index 頁面
4.「/」,當什麼都匹配不到,就會自動進來,但 context 還是 index 頁面
5.嚴格匹配:從頭到尾都一樣才可以

以上的 5 條, url-pattern 可以寫多個,但要注意以下兩點:
1.不可以將前兩條合成一個 /xxx/ooo/*.abc,伺服器可以啟動,但匹配不到
可以寫兩個 url-pattern,如 /xxx/ooo/* 和 *.abc,不過意思不一樣

2.「/*」什麼都匹配的到,不會有 index 頁面
「*」:伺服器無法啟動

.context 就是設定 context path 的路徑,可以為空

.http://localhost:8080/servletContext/ --> 只要沒配空或「/*」就會連到 index 頁面,tomcat 有個 conf/web.xml,如果伺服器沒有寫 welcome-file-list,就會以這裡為主,先找到先用

沒有留言:

張貼留言