###### 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 直接翻譯:代表性的狀態轉移
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.