SpringBoot定義URL處理方法:@Controller和@RequestMapping
@Controller標注的類表示的是一個處理HTTP請求的控制器(即MVC中的C),該類中所有被@RequestMapping標注的方法都會用來處理對應URL的請求。
在SpringMVC框架中,使用@RequsetMapping標注可以將URL與處理方法綁定起來,例如:
@RestController public class HelloworldRestController { @RequestMapping("/") public String helloworld(){ return "hello world"; } @RequestMapping("/hello") @ResponseBody public String hello(){ return "fpc"; } }
HelloworldRestController類被@Controller標注,其中的兩個方法都被@RequestMapping標注,當應用程序運行后,在瀏覽器中訪問:localhost:8089,請求會被SpringMVC框架分發到hellworld()方法進行處理。同理輸入localhost:8089/hello會交給hello()方法處理。
@ResponseBody標注表示處理函數直接將函數的返回值傳到瀏覽器端顯示。
運行結果:
輸入localhost:8089:
輸入loalhost:8089/hello:
@RequestMapping標注類
@RequestMapping標注同樣可以加在類上:
@RestController @RequestMapping("/index") public class HelloworldRestController { @RequestMapping("/") public String helloworld(){ return "hello world"; } @RequestMapping("/hello") @ResponseBody public String hello(){ return "fpc"; } }
hello()方法綁定的URL路徑是/index/hello
運行結果:
如果直接訪問:localhost:8089/hello:
如果訪問:localhost:8089/index/hello:
提示:每一個類都可以包含一個或者多個@RequestMapping標注的方法,通常我們會將業務邏輯相近的URL放在同一個Controller中處理。
@RequestMapping的簡寫形式
在web應用中常用的HTTP方法有四種:
- PUT方法用來添加資源
- GET方法用來獲取已有的資源
- POST方法用來對資源進行狀態轉換
- DELETE方法用來刪除已有的資源
這四個方法可以對應到CRUD操作(Create,Read,Update和Delete)比如博客的創建操作,按照REST風格設計URL就應該使用PUT方法,讀取博客使用GET方法,更新博客使用POST方法,刪除博客使用DELETE方法。
每一個Web請求都是屬於其中一種,在SpringMVC中如果不特殊指定的話,默認是GET請求。
比如@RequestMapping("/")和@RequestMapping("/hello")和對應的Web請求是:
- GET /
- GET /hello
實際上@RequestMapping("/")是@RequestMapping("/",method = RequestMethod.GET)的簡寫,即可以通過method屬性,設置請求的HTTP方法。
比如PUT /hello請求,對應@RequestMapping("/hello",method = RequestMethod.PUT)
Spring MVC最新的版本中提供了一種更加簡潔的配置HTTP方式,增加了四個標注:
- PutMapping
- GetMapping
- PostMapping
- DeleteMapping
基於新的標注@RequestMapping("/hello",method = RequestMethod.PUT) 可以簡寫成@PutMapping("/hello"),@RequestMapping("/hello")和@GetMapping("/hello")等價。
返回HTML
在之前所有的Controller方法中,返回值字符串被直接傳送到瀏覽器端並顯示給用戶。但是為了能夠呈現更加豐富,美觀的頁面,我們需要將HTML代碼返回給瀏覽器,瀏覽器在進行頁面的渲染顯示,一種很直觀的方法是在處理請求的方法中,直接返回HTML代碼:
@RequestMapping("/blog") public String blog(){ return "<html><head><title>Title</title></head><body><h2>This is a blog</h2><p>This is content of the blog.</p></body></html>"; }
運行結果:瀏覽器地址欄輸入:localhost:8089/blog:
顯然,這樣做的問題在於一個復雜的頁面的HTML代碼往往也非常復雜,並且內嵌在Java代碼中十分不利於維護。
更好的做法是將頁面的HTML代碼卸載模板文件中,然后讀取該文件並返回。Spring天然支持這種非常常見的場景,需要現在pom.xml引入Thymelea依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
將HTML文本保存在:src/main/webapp/pages/blog.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>This is title</h1> <p>This is Content.</p> </body> </html>
Controller中可以去掉@ResponseBody標注(表示不是直接返回字符串,而是返回渲染的HTML模板,並將URL處理函數設置為剛剛保存在pages/文件夾中的文件名(不需要擴展名:))
運行結果並沒有出現返回blog.html頁面,而是返回了"blog"字符串在瀏覽器中顯示出來:
造成這種現象的原因是:如果類是用@RestController修飾的出現這種情況很正常,如果想要返回頁面可以將@RestController改為@Controller
package springboot; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @Controller //@RequestMapping("/index") public class HelloworldRestController { @RequestMapping("/") public String helloworld(){ return "hello world"; } @RequestMapping("/hello") @ResponseBody public String hello(){ return "fpc"; } @RequestMapping("/blog") public String blog(){ return "blog"; } }
繼續運行項目:又出現問題了,報錯信息為:
org.xml.sax.SAXParseException: 元素類型 "meta" 必須由匹配的結束標記 "</meta>" 終止。
報錯的原因是blog.html代碼中,meta元素沒有封閉:
你可以直接封閉meta元素:
<meta charset="UTF-8"/>
也可修改SpringBoot的配置文件application.properties:
spring.thymeleaf.mode=LEGACYHTML5
SpringBoot的配置文件通常在/resource根目錄下,以application.properties命名,沒有這個文件則創建一個。
另外為了保證Thymeleaf能夠正確識別HTML5,還需要添加Maven依賴到pom.xml:
<dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.22</version> </dependency>
再次運行程序:
注意:在編寫HTML代碼時,請務必保證每一個標簽都是閉合的,容易忽略的標簽包括<meta/>,<link/>,<br/><hr/>,<img/>,<input/>等等
但是在HTML5中,有些標簽並不要求閉合,Thymeleaf遇到這樣的HTML文件會報錯。為了支持HTML5,你可以在SpringBoot配置文件中增加一行配置:如上面的過程。
靜態資源處理
在編寫HTML代碼的過程中,我們會遇到幾類外部靜態資源:
- CSS文件:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"/>
- JavaScript文件:
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
- 圖像:
<img src="http://assets0.tianmaying.com/img/appicon/ios.png"/>
這些外部資源都是通過HTTP協議訪問得到--也就是說,當我們用瀏覽器打開我們編寫的HTML頁面(無論是通過本地文件直接打開,還是訪問SpringBoot服務器),在獲取頁面內容本身之外,還需要向外部服務器(例如maxcdn.bootstrapcdn.com)發起HTTP請求以獲取我們需要的CSS/JavaScript資源。
但是在我們開發過程中,如果某個時刻不能訪問Internet,那我們的頁面也就無法正確地展現出它應有的樣式。另一方面,除了使用第三方庫,我們自己還會編寫大量的CSS/JavaScript文件,這就要求我們必須有一種很快的方式能夠在修改后立馬在本地看到結果。
本地資源文件組織
首先我們拋開本地HTTP服務器,簡單來看在本地編寫一個HTML文件以及使用CSS資源,那么我們可以這樣組織項目結構:
.
├── index.html
├── css
└── style.css
└── js
└── main.js
在index.html文件中你可以這樣引用它們:
<link rel="stylesheet" href="css/style.css"/> <script src="js/main.js"></script>
css/style.css和js/main.js都是使用相對路徑描述。
服務器中的靜態資源文件
如果需要將index.html放在服務器中呢?index.html位於templates目錄下,通過http://localhost:8089/可以訪問首頁內容,但是CSS和JavaScript外部資源呢?因為我們的HTTP服務器根本沒有處理它們,所以不可能通過類似http://localhost:8089/css/style.css這樣的方式來訪問他們使得我們的頁面正確顯示。
默認情況下,SpringBoot會將類路徑上的/static/目錄的內容Serve起來,意思就是對靜態資源的請求,都會返回/static/目錄中對應路徑的文件內容,於是我們可以這樣組織文件目錄結構來處理靜態資源(以下是src/main/resources 目錄結構,這個目錄經過編譯后被添加到類路徑上)
├── static
├── css
└── style.css
└── js
└── main.js
└── templates
└── index.html
這樣,當我們經過以上布局,重啟應用后,就可以通過訪問http://localhost:8089/css/style.css和http://localhost:8089/js/main.js來獲取CSS和JavaScript資源了。
在HTML中引入資源
最后我們將靜態資源引入到HTML頁面中,我們往往需要一種介於相對路徑(css/style.css)和絕對路徑(http://localhost:8089/css/style.css)之間的資源訪問方式--context路徑:
<link rel="stylesheet" href="/css/style.css"/> <script src="/js/main.js"></script>
這里只是簡單的在相對路徑URL的最前面加上了/,但是意義和相對路徑就完全不同了,此時服務器會將其視為訪問當前host中的“絕對路徑”,也就是自動在這個路徑前面加上協議,主機名,端口(都是當前服務器的相同信息),那么無論我們訪問的是當前網站下的任何路徑,它都會給出統一的結果,從而正確引用到外部資源。