2016年3月7日 星期一

編碼、web-fragment.xml、三種對應、生命週期、Servlet重載、初始參數 (Servlet 二)

※編碼

.HttpServletRequest是接收html、jsp的編碼,又分成get和post編碼

.HttpServletResponse是回傳給瀏覽器的,所以以ie來說,檢視-->編碼會是resp.setContentType("text/html;charset=Big5");的值


※HttpServletResponse

要注意resp.setContentType設定Big5,那麼getContentType和getCharacterEncoding()都是Big5
可是setCharacterEncoding("Big5"),只有getCharacterEncoding()是Big5,另外一個是null
至少我試tomcat8是這樣



※HttpServletRequest

※post編碼

jsp的
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
也可以寫成
<%@ page language="java" contentType="text/html" pageEncoding="UTF-8"%>
但如果寫成
<%@ page language="java" contentType="text/html; charset=Big5" pageEncoding="UTF-8"%>
那瀏覽器最終的結果就是Big5
可參考良葛格的網頁

pageEncoding是指這支jsp用的編碼(在Eclipse裡改好存檔後,按右鍵Properties就可以知道),而contentType的charset是給瀏覽器用的,當然可以不一樣
charset和pageEncoding如果其中一個沒有編碼,那沒打編碼的會和有打編碼的一樣
如果兩個都沒打,那就是ISO8859-1
※以上的說法,我是用tomcat7和8的最後一版測的,瀏覽器有沒有差我不知道,我用IE11和Chrome 結果都一樣

req.setCharacterEncoding("UTF-8");兩個編碼要一樣,這樣用戶傳中文進來才不會亂碼(當然也要支援中文,如果傳ISO-8859-1是一定亂碼的)

1.使用req.setCharacterEncoding(和前端一樣的編碼名稱),這行以下的部分只要使用req.getParameter就會使用指定的編碼

2.使用new String(req.getParameter("hid").getBytes("ISO-8859-1"), 和前端一樣的編碼名稱),這是針對這一次的,所以不會影響到之後的編碼,
因為預設是ISO-8859-1,所以可拿來轉換

3.用Eclipse生成jsp會有<meta http-equiv="Content-Type" content="text/html; charset=Big5">
這是HTML的寫法,但副檔是jsp的話,這行有寫沒寫都沒差,除非副檔名是html



※get編碼

使用req.setCharacterEncoding()沒有用,還是會亂碼,要注意指的是req抓取的部分,如下:
System.out.println("中文:" + req.getParameter("hid"));
這時「中文」兩個字不會出亂碼,只有req取前端的資料會出亂碼

使用 new String(req.getParameter("hid").getBytes("ISO-8859-1", 和前端一樣的編碼名稱)
因為預設是ISO-8859-1(tomcat7不知哪個版本之後~第9版改成UTF-8了),所以可拿來轉換

Tomcat官網的左邊
左邊Documentation的Tomcat x.0
左邊Reference的Configuration
左邊Connectors的HTTP
Common Attributes有個URIEncoding
在這裡
按照這樣的找法,會發現6的預設值是ISO-8859-1,7~9是UTF-8(還有但書,自己看)
這個設定要在server.xml的Connector,找到沒有註解的,我找到的只有兩個,要選protocol="HTTP/1.1"的(因為剛剛找的就是HTTP)
把它改成URIEncoding="ISO-8859-1"會和6一樣,所以就不會有亂碼了,這個設定我試的結果,不會影響post
我有把它改成new String(req.getParameter("hid").getBytes("UTF-8", 和前端一樣的編碼名稱),但沒有用,不知道為什麼
良葛格也有寫,但他沒說為什麼
現在的網頁大部分都用UTF-8了,所以在7~9全部都設UTF-8就不會感受到get和post的不同了

還有一個屬性是useBodyEncodingForURI,預設是false
這個設定表示get的設定要不要和post一樣

按照上面的找法找到Configuration後,左邊web.xml的Filter,6~9都有Set Character Encoding Filter
它說可以用filter,名稱是org.apache.catalina.filters.SetCharacterEncodingFilter,所以在web.xml多一些設定,如下:
<filter>
    <filter-name>xxx</filter-name>
    <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>Big5</param-value>
    </init-param>
</filter>
    
<filter-mapping>
    <filter-name>xxx</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

※這個設定post很ok,但get還是要設URIEncoding,而且編碼要和web.xml、jsp一樣才可以

※filter後面會再介紹



※web-fragment.xml

類似web.xml的功能有三種第一篇有講到annotation,還有一種叫web-fragment.xml,都是Servlet3.0增加的

而web.xml的<web-app>有一個屬性叫metadata-complete,意思是設定web.xml是否完整,預設是false,表示不完整,所以可以參考web-fragment.xml、annotation的設定,如果設定一樣,以web.xml為主,如果設定為true,那web-fragment.xml、annotation就沒作用了

優先順序是web.xml-->web-fragment.xml-->annotation
良葛格這篇寫的很好

Eclipse可以用滑鼠創建,File-->New-->Other,如下:




Project name取個名字
Dynamic Web project membership裡面的專案名稱是指目前創的這個Web Fragment要給哪一個專案用
Finish即可

在TestServlet專案右鍵-->Properties 如下操作後可看到剛剛創的Web Fragment

上面的紅框是剛剛建的,下面的紅框是抓到的

裡面的內容基本上和web.xml一樣,但一般來說url-pattern一樣會報錯,下面的三種對應會說,可是在這裡,我如果設一樣,不會報錯,它抓到的會是web.xml



※三種對應

<servlet-mapping>
    <servlet-name>ooo</servlet-name>
    <url-pattern>/xxx</url-pattern>
</servlet-mapping>

※這幾種對應,jsp都不用「/」開頭,也不是真的錯,是路徑不一樣,第一篇有講過

※第一篇的對應是完全對應,也就是jsp的action是xxx就會對應到

※第二種是前置路徑對應/xxx/*(不一定要用「*」),只要是xxx開頭的就對應的到,
沒有/xxx/*/ooo這種寫法

※最後一種是副檔名對應*.xxx(一定要有「.」),只要是.xxx結尾的就對應的到,
沒有.xxx這種寫法

※如果都針測到,以第一種優先,第二種次之,最後是第三種,還有一個是全部處理的「/*」,它的優先權最高,比完全對應還高,如果想測試,可再寫一隻Servlet,如下:
<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>
    
    
<servlet>
    <servlet-name>aaa</servlet-name>
    <servlet-class>controller.LoadOnStartup</servlet-class>
</servlet>
    
<servlet-mapping>
    <servlet-name>aaa</servlet-name>
    <url-pattern>/xxx/ooo</url-pattern>
</servlet-mapping>

※裡面印出一些東西就知道run的是哪一支了,只會run一支哦!並不是優先權最高run完,然後run優先權低的

※要注要url-pattern不能和其他servlet完全一樣,如果這兩支用一樣的,就會出「The servlets named [ooo] and [aaa] are both mapped to the url-pattern [/xxx] which is not permitted」的錯

※這次的結果是*優先明確打出來的ooo



※生命週期

1.Servlet Reloading
2..Load on Startup
3.Init Parameter
4.Synchronization
5.initial & Destory

Servlet container替我們建立servlet實體;並且,由其控制呼叫init、service、destroy等方法,以管理servlet的生命週期

Servlet container一般執行步驟
當Servlet第一次被客戶端請求時,它會載入該Servlet,建立實體
呼叫Servlet的init(),以進行Servlet初始化工作
呼叫Servlet的service(),處理所有請求
當container關閉時,會呼叫每一個Servlet的destroy()


重載入上一篇有講設定了,這裡寫一支程式測一下
※隨便寫一支Servlet如下:
int i = 0;
    
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    i++;
    PrintWriter out = resp.getWriter();
    out.println("a=" + i);
}

※這樣子i的值會一直增加,不管有沒有換瀏覽器都會,甚至關掉都會一直增加 

※但如果修改了Servlet後又會重新開始

※load-on-startup


<servlet>
    <servlet-name>ooo</servlet-name>
    <servlet-class>controller.HelloServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

※根據文件的101、102頁表示要打0以上(包括0)的整數,數字越小越優先載入

※只要有設就表示在server啟動時就會載入,不然就會等拜訪時才會載入

※如果多支都設一樣的數字不會錯,只是順序就不一定了



※Init Parameter


※可以定義初始的參數,web.xml如下設定
<servlet>
    <servlet-name>ooo</servlet-name>
    <servlet-class>controller.HelloServlet</servlet-class>
    <init-param>
        <param-name>p1</param-name>
        <param-value>xxx</param-value>
    </init-param>
    <init-param>
        <param-name>p2</param-name>
        <param-value>20</param-value>
    </init-param>
</servlet>

※定義了p1和p2,可以很多


@Override
public void init(ServletConfig config) throws ServletException {
    Enumeration<String> en = config.getInitParameterNames();
    while(en.hasMoreElements()){
        System.out.println("有參:" + en.nextElement());
    }
    System.out.println("有參:" + config.getInitParameter("p1"));
}
    
@Override
public void init() throws ServletException {
    Enumeration<String> en = getInitParameterNames();
    while(en.hasMoreElements()){
        System.out.println("無參:" + en.nextElement());
    }
    System.out.println("無參:" + getInitParameter("p1"));
}

※取值時,寫在init裡,但init有兩個,一個有參(Servlet)、一個無參(GenericServlet),都可以取的到值,但兩個都寫只會run到有參的

※如果名稱亂打,取不到,當然就是null

※annotation這樣子用
@WebServlet(
    urlPatterns = { "/ooo" },
    loadOnStartup = 1,
    initParams = {
        @WebInitParam(name = "p1", value = "xxx"),
        @WebInitParam(name = "p2", value = "20")
    }
)

沒有留言:

張貼留言