###### tags: `Servlet&JSP`
# MVC & URL
## MVC
在 MVC 中,將 Web 應用程式劃分為模型、畫面與控制器:
* M模型(Model)的職責
* 保存應用程式狀態
* 執行應用程式商務邏輯(Business logic)
* 只要跟資料庫有關
* 細分成Dao、Bean、Servers 等等
* V畫面(View)的職責
* 提取模型狀態
* 執行呈現邏輯(Presentation logic)組織回應畫面
* C控制器(Controller)的職責
* 接受請求
* 驗證請求
* 判斷要轉發請求給哪個模型
* 判斷要轉發請求給哪個畫面
大致的流程示意如下所示:

例子:依使用者發送的名稱來提取個別訊息顯示。
```
package cc.openhome;
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;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
private Hello hello = new Hello();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("user");
String message = hello.doHello(name);
request.setAttribute("message", message);
request.getRequestDispatcher("hello.jsp").forward(request, response);
}
}
```
其中 Hello 的職責劃分是模型,根據使用者的 user 請求參數不同,會取得不同的訊息給 message 參考:
```
package cc.openhome;
import java.util.*;
public class Hello {
private Map<String, String> messages;
public Hello() {
messages = new HashMap<>();
messages.put("caterpillar", "Hello");
messages.put("Justin", "Welcome");
messages.put("momor", "Hi");
}
public String doHello(String user) {
return String.format("%s, %s!", messages.get(user), user);
}
}
```
依上,如果 user 請求參數是 caterpillar 就會取得 "Hello" 字串,如果是 Justin,就會取得 "Welcome" 字串,在取得訊息之後,先前擔任控制器的 Servlet 會轉發給畫面:
```
<!DOCTYPE html>
<html>
<head>
<title>${param.user}</title>
</head>
<body>
<h1>${message}</h1>
</body>
</html>
```
畫面會取得使用者的訊息,並使用 HTML 來顯示,所以,如果你的請求是:
```
http://localhost:8080/ServletDemo/hello?user=caterpillar
```
得到以下的 HTML:
```
<!DOCTYPE html>
<html>
<head>
<title>caterpillar</title>
</head>
<body>
<h1>Hello, caterpillar!</h1>
</body>
</html>
```
經由適當的職責劃分,控制器與模型不用處理 HTML 的輸出,而 JSP 也不會被 Java 程式碼干擾,複雜的情境時MCV各司其責,使得程式更易於維護。
**注意** : 以上只是範例別直接放到你實際上線的程式之中,這個簡單的程式中,由於 JSP 頁面直接將使用者的請求參數輸出,如果使用者懷著惡意輸入了一些不該輸入的東西,例如 JavaScript 程式,就可以製造某種形式的攻擊了。
---
## URL模式
### 一個請求 URI 實際上是由三個部份所組成
```
requestURI = contextPath + servletPath + pathInfo
```
可以使用 HttpServletRequest 的 getRequestURI() 來取得。
其中 contextPath 是環境路徑(Context path),是容器用來決定該挑選哪個 Web 應用程式的依據(一個容器上可能部署多個Web應用程式),在 Servlet 4.0 之前,環境路徑的設定並沒有標準規範,而是依伺服器實作而有所不同。例如在 Tomcat 9 中,可以設定 context.xml 的 <Context path="xxxx"> 來決定,在 Servlet 4.0 中,可以於 web.xml 中使用 <default-context-path> 來設定預設路徑,然而,容器實作廠商可以覆蓋這個設定。
你可使用 HttpServletRequest 的 getContextPath() 來取得。如果應用程式環境路徑與 Web 伺服器環境根路徑相同,則應用程式環境路徑為空字串,如果不是,則應用程式環境路徑以 / 開頭,不包括 / 結尾。
一旦決定是哪個 Web 應用程式來處理請求,接下來就進行 Servlet 的挑選,Servlet 必須設定 URL 模式(URL pattern),可以設定的格式優先序如下:
### 1.Exact match 明確の對應關係(嚴格匹配)
字串完全吻合且不含 * 號。
例如若 URL 模式設定為/HelloWorld.do。
servletPath為/HelloWorld.do。
### 2.Path mapping 前置路徑の對應關係
/ 開頭但 /* 結尾的 URL 模式。
例如若設定 URL 模式為 /guest/*,則請求 URI 扣去環境路徑的部份若為/guest/test.view
、/guest/home.view 等以 /guest/ 作為開頭的,都會交由該Servlet處理。
servletPath為/guest。
PathInfo為/*查到的東東。(只有Path mapping有PathInfo!)
### 3.Extension mapping 延伸檔名の對應關係
以 *. 開頭的 URL 模式。
例如若 URL 模式設定為 *.view,則所有以 .view 結尾的請求,都會交由該 Servlet 處理。
servletPath為/guest/test.view。
### 4.Context root 預設の對應關係
僅包括 / 的 URL 模式。
當找不到適合的 URL 模式對應時,就才會使用預設 Servlet。

在最上面的 requestURI 中,servletPath 的部份是指 Servlet 路徑(Servlet path),不包括路徑資訊(Path info)與請求參數(Request parameter)。Servlet 路徑直接對應至URL模式資訊,可使用 HttpServletRequest 的 getServletPath() 來取得,Servlet 路徑基本上是以 / 開頭,但 /* 與空字串的URL模式比對而來的請求除外,在 /* 與空字串的情況下,Servlet 路徑是 ""。
例如若某個請求是根據 /hello.do 對應至 Servlet,則 Servlet 路徑就是 "/hello.do",如果是透過 /servlet/* 對應至 Servlet,則 Servlet 路徑就是 "/servlet",但如果是透過 /* 或空字串,則 Servlet 路徑就是 ""。
requestURI 中 pathInfo 是指路徑資訊(Path info),路徑資訊不包括請求參數,指的是不包括 Context Path 與 Servlet Path 部份的額外路徑資訊。
可使用 HttpServletRequest 的 getPathInfo() 來取得。如果沒有額外路徑資訊,則為 null(延伸對應、預設 Servlet、嚴格匹配的情況下),如果有額外路徑資訊,則是個以 "/" 為開頭的字串。
好累剩下的翻這個後面:
https://openhome.cc/Gossip/ServletJSP/URLPattern.html
REST 全名 REpresentational State Transfer
直接翻譯:代表性的狀態轉移