一暇韧、REST簡介
REST?(Representational State Transfer)叶摄,表現(xiàn)形式狀態(tài)轉(zhuǎn)換,它是一種軟件架構(gòu)?風(fēng)格
當(dāng)我們想表示一個網(wǎng)絡(luò)資源的時候趟佃,可以使用兩種方式:
傳統(tǒng)風(fēng)格資源描述形式
http://localhost/user/getById?id=1?查詢id為1的用戶信息
http://localhost/user/saveUser?保存用戶信息
REST風(fēng)格描述形式
http://localhost/user/1
http://localhost/user
傳統(tǒng)方式一般是一個請求url對應(yīng)一種操作客税,這樣做不僅麻煩惨恭,也不安全,因為會程序的人讀取了你的請求url地址弹惦,就大概知道該url實現(xiàn)的是一個什么樣的操作否淤。
查看REST風(fēng)格的描述悄但,你會發(fā)現(xiàn)請求地址變的簡單了棠隐,并且光看請求URL并不是很能猜出來該URL的具體功能
所以REST的優(yōu)點有:
隱藏資源的訪問行為,無法通過地址得知對資源是何種操作
書寫簡化
但是我們的問題也隨之而來了檐嚣,一個相同的url地址即可以是新增也可以是修改或者查詢助泽,那么到底我們該如何區(qū)分該請求到底是什么操作呢?
按照REST風(fēng)格訪問資源時使用?行為動作?區(qū)分對資源進(jìn)行了何種操作
http://localhost/users?查詢?nèi)坑脩粜畔?采用GET請求(查詢)
http://localhost/users/1?查詢指定用戶信息 采用GET請求(查詢)
http://localhost/users?添加用戶信息 采用POST請求(新增/保存)
http://localhost/users?修改用戶信息 采用PUT請求(修改/更新)
http://localhost/users/1?刪除用戶信息 采用DELETE請求(刪除)
請求的方式比較多,但是比較常用的就4種嚎京,分別是?GET?,?POST?,?PUT?,?DELETE?嗡贺。
按照不同的請求方式代表不同的操作類型。
發(fā)送GET請求是用來做查詢
發(fā)送POST請求是用來做新增
發(fā)送PUT請求是用來做修改
發(fā)送DELETE請求是用來做刪除
注意:
上述行為是約定方式鞍帝,約定不是規(guī)范诫睬,可以打破,所以稱REST風(fēng)格帕涌,而不是REST規(guī)范
REST提供了對應(yīng)的架構(gòu)方式摄凡,按照這種架構(gòu)設(shè)計項目可以降低開發(fā)的復(fù)雜性续徽,提高系統(tǒng)的可伸縮性
REST中規(guī)定GET/POST/PUT/DELETE針對的是查詢/新增/修改/刪除,但是我們?nèi)绻且肎ET請求做刪除亲澡,這點在程序上運行是可以實現(xiàn)的
描述模塊的名稱通常使用復(fù)數(shù)钦扭,也就是加s的格式描述,表示此類資源床绪,而非單個資源客情,例如:users、books癞己、accounts......
清楚了什么是REST風(fēng)格后膀斋,我們后期會經(jīng)常提到一個概念叫?RESTful?,那什么又是RESTful呢?
根據(jù)REST風(fēng)格對資源進(jìn)行訪問稱為?RESTful?痹雅。
后期我們在進(jìn)行開發(fā)的過程中概页,大多是都是遵從REST風(fēng)格來訪問我們的后臺服務(wù),所以可以說咱們以后都是基于RESTful來進(jìn)行開發(fā)的练慕。
二惰匙、RESTful入門案例
2.1 環(huán)境準(zhǔn)備
創(chuàng)建一個Web的Maven項目
參考?Java開發(fā)學(xué)習(xí)(二十三)----SpringMVC入門案例、工作流程解析及設(shè)置bean加載控制?中環(huán)境準(zhǔn)備
pom.xml添加Spring依賴
<?xml version="1.0"encoding="UTF-8"?>4.0.0com.itheimaspringmvc_06_rest1.0-SNAPSHOTwarjavax.servletjavax.servlet-api3.1.0providedorg.springframeworkspring-webmvc5.2.10.RELEASEcom.fasterxml.jackson.corejackson-databind2.9.0org.apache.tomcat.maventomcat7-maven-plugin2.180/
創(chuàng)建對應(yīng)的配置類
publicclassServletContainersInitConfigextendsAbstractAnnotationConfigDispatcherServletInitializer{protectedClass[] getRootConfigClasses() {returnnewClass[0];? ? }protectedClass[] getServletConfigClasses() {returnnewClass[]{SpringMvcConfig.class};? ? }protectedString[] getServletMappings() {returnnewString[]{"/"};? ? }//亂碼處理@OverrideprotectedFilter[] getServletFilters() {CharacterEncodingFilterfilter =newCharacterEncodingFilter();? ? ? ? filter.setEncoding("UTF-8");returnnewFilter[]{filter};? ? }}@Configuration@ComponentScan("com.itheima.controller")//開啟json數(shù)據(jù)類型自動轉(zhuǎn)換@EnableWebMvcpublicclassSpringMvcConfig{}
編寫模型類User和Book
publicclassUser{privateString name;privateintage;//getter...setter...toString省略}publicclassBook{privateString name;privatedoubleprice;//getter...setter...toString省略}
編寫UserController和BookController
@ControllerpublicclassUserController{@RequestMapping("/save")@ResponseBodypublicString save(@RequestBodyUser user) {? ? ? ? System.out.println("user save..."+user);return"{'module':'user save'}";? ? }@RequestMapping("/delete")@ResponseBodypublicString delete(Integer id) {? ? ? ? System.out.println("user delete..."+ id);return"{'module':'user delete'}";? ? }@RequestMapping("/update")@ResponseBodypublicString update(@RequestBodyUser user) {? ? ? ? System.out.println("user update..."+ user);return"{'module':'user update'}";? ? }@RequestMapping("/getById")@ResponseBodypublicString getById(Integer id) {? ? ? ? System.out.println("user getById..."+ id);return"{'module':'user getById'}";? ? }@RequestMapping("/findAll")@ResponseBodypublicString getAll() {? ? ? ? System.out.println("user getAll...");return"{'module':'user getAll'}";? ? }}@ControllerpublicclassBookController{@RequestMapping(value ="/books",method = RequestMethod.POST)@ResponseBodypublicString save(@RequestBodyBook book){? ? ? ? System.out.println("book save..."+ book);return"{'module':'book save'}";? ? }@RequestMapping(value ="/books/{id}",method = RequestMethod.DELETE)@ResponseBodypublicString delete(@PathVariableInteger id){? ? ? ? System.out.println("book delete..."+ id);return"{'module':'book delete'}";? ? }@RequestMapping(value ="/books",method = RequestMethod.PUT)@ResponseBodypublicString update(@RequestBodyBook book){? ? ? ? System.out.println("book update..."+ book);return"{'module':'book update'}";? ? }@RequestMapping(value ="/books/{id}",method = RequestMethod.GET)@ResponseBodypublicString getById(@PathVariableInteger id){? ? ? ? System.out.println("book getById..."+ id);return"{'module':'book getById'}";? ? }@RequestMapping(value ="/books",method = RequestMethod.GET)@ResponseBodypublicString getAll(){? ? ? ? System.out.println("book getAll...");return"{'module':'book getAll'}";? ? }? ? }
最終創(chuàng)建好的項目結(jié)構(gòu)如下:
2.2 思路分析
需求:將增刪改查替換成RESTful的開發(fā)方式铃将。
1.不同的請求有不同的路徑,現(xiàn)在要將其修改為統(tǒng)一的請求路徑
修改前: 新增: /save ,修改: /update,刪除 /delete...
修改后: 增刪改查: /users
2.根據(jù)GET查詢项鬼、POST新增、PUT修改劲阎、DELETE刪除對方法的請求方式進(jìn)行限定
3.發(fā)送請求的過程中如何設(shè)置請求參數(shù)?
2.3 修改成為RESTful風(fēng)格
新增
@ControllerpublicclassUserController{//設(shè)置當(dāng)前請求方法為POST绘盟,表示REST風(fēng)格中的添加操作@RequestMapping(value ="/users",method = RequestMethod.POST)@ResponseBodypublicString save() {? ? ? ? System.out.println("user save...");return"{'module':'user save'}";? ? }}
將請求路徑更改為?/users
訪問該方法使用 POST:?http://localhost/users
使用method屬性限定該方法的訪問方式為?POST
如果發(fā)送的不是POST請求,比如發(fā)送GET請求悯仙,則會報錯
刪除
@ControllerpublicclassUserController{//設(shè)置當(dāng)前請求方法為DELETE龄毡,表示REST風(fēng)格中的刪除操作@RequestMapping(value ="/users",method = RequestMethod.DELETE)@ResponseBodypublicString delete(Integer id) {? ? ? ? System.out.println("user delete..."+ id);return"{'module':'user delete'}";? ? }}
將請求路徑更改為?/users
訪問該方法使用 DELETE:?http://localhost/users
訪問成功,但是刪除方法沒有攜帶所要刪除數(shù)據(jù)的id,所以針對RESTful的開發(fā)锡垄,如何攜帶數(shù)據(jù)參數(shù)?
傳遞路徑參數(shù)
前端發(fā)送請求的時候使用:?http://localhost/users/1?,路徑中的?1?就是我們想要傳遞的參數(shù)沦零。
后端獲取參數(shù),需要做如下修改:
修改@RequestMapping的value屬性货岭,將其中修改為?/users/{id}?路操,目的是和路徑匹配
在方法的形參前添加@PathVariable注解
@ControllerpublicclassUserController{//設(shè)置當(dāng)前請求方法為DELETE,表示REST風(fēng)格中的刪除操作@RequestMapping(value ="/users/{id}",method = RequestMethod.DELETE)@ResponseBodypublicString delete(@PathVariableInteger id) {? ? ? ? System.out.println("user delete..."+ id);return"{'module':'user delete'}";? ? }}
思考如下兩個問題:
(1)如果方法形參的名稱和路徑?{}?中的值不一致千贯,該怎么辦?
(2)如果有多個參數(shù)需要傳遞該如何編寫?
前端發(fā)送請求的時候使用:?http://localhost/users/1/tom?,路徑中的?1?和?tom?就是我們想要傳遞的兩個參數(shù)屯仗。
后端獲取參數(shù),需要做如下修改:
@ControllerpublicclassUserController{//設(shè)置當(dāng)前請求方法為DELETE搔谴,表示REST風(fēng)格中的刪除操作@RequestMapping(value ="/users/{id}/{name}",method = RequestMethod.DELETE)@ResponseBodypublicString delete(@PathVariableInteger id,@PathVariableString name) {? ? ? ? System.out.println("user delete..."+ id+","+name);return"{'module':'user delete'}";? ? }}
修改
@ControllerpublicclassUserController{//設(shè)置當(dāng)前請求方法為PUT魁袜,表示REST風(fēng)格中的修改操作@RequestMapping(value ="/users",method = RequestMethod.PUT)@ResponseBodypublicString update(@RequestBodyUser user) {? ? ? ? System.out.println("user update..."+ user);return"{'module':'user update'}";? ? }}
將請求路徑更改為?/users
訪問該方法使用 PUT:?http://localhost/users
訪問并攜帶參數(shù):
根據(jù)ID查詢
@ControllerpublicclassUserController{//設(shè)置當(dāng)前請求方法為GET,表示REST風(fēng)格中的查詢操作@RequestMapping(value ="/users/{id}",method = RequestMethod.GET)@ResponseBodypublicString getById(@PathVariableInteger id){? ? ? ? System.out.println("user getById..."+id);return"{'module':'user getById'}";? ? }}
將請求路徑更改為?/users
訪問該方法使用 GET:?http://localhost/users/666
查詢所有
@ControllerpublicclassUserController{//設(shè)置當(dāng)前請求方法為GET,表示REST風(fēng)格中的查詢操作@RequestMapping(value ="/users",method = RequestMethod.GET)@ResponseBodypublicString getAll() {? ? ? ? System.out.println("user getAll...");return"{'module':'user getAll'}";? ? }}
將請求路徑更改為?/users
訪問該方法使用 GET:?http://localhost/users
小結(jié)
RESTful入門案例峰弹,我們需要學(xué)習(xí)的內(nèi)容如下:
(1)設(shè)定Http請求動作(動詞)
@RequestMapping(value="",method = RequestMethod.POST|GET|PUT|DELETE)
(2)設(shè)定請求參數(shù)(路徑變量)
@RequestMapping(value="/users/{id}",method = RequestMethod.DELETE)
@ReponseBody
public String delete(@PathVariable Integer id){
}
知識點1:@PathVariable
名稱@PathVariable
類型形參注解
位置SpringMVC控制器方法形參定義前面
作用綁定路徑參數(shù)與處理器方法形參間的關(guān)系距境,要求路徑參數(shù)名與形參名一一對應(yīng)
關(guān)于接收參數(shù)三個注解?@RequestBody?、?@RequestParam?垮卓、?@PathVariable?,這三個注解之間的區(qū)別和應(yīng)用分別是什么?
區(qū)別
@RequestParam用于接收url地址傳參或表單傳參
@RequestBody用于接收json數(shù)據(jù)
@PathVariable用于接收路徑參數(shù)垫桂,使用{參數(shù)名稱}描述路徑參數(shù)
應(yīng)用
后期開發(fā)中,發(fā)送請求參數(shù)超過1個時粟按,以json格式為主诬滩,@RequestBody應(yīng)用較廣
如果發(fā)送非json格式數(shù)據(jù),選用@RequestParam接收請求參數(shù)
采用RESTful進(jìn)行開發(fā)灭将,當(dāng)參數(shù)數(shù)量較少時疼鸟,例如1個,可以采用@PathVariable接收請求路徑變量庙曙,通常用于傳遞id值
三空镜、RESTful快速開發(fā)
做完了RESTful的開發(fā),你會發(fā)現(xiàn)?好麻煩?捌朴,麻煩在哪?
每個方法的@RequestMapping注解中都定義了訪問路徑/books吴攒,重復(fù)性太高。
每個方法的@RequestMapping注解中都要使用method屬性定義請求方式砂蔽,重復(fù)性太高洼怔。
每個方法響應(yīng)json都需要加上@ResponseBody注解,重復(fù)性太高左驾。
對于上面所提的這三個問題镣隶,具體該如何解決?
@RestController//@Controller + ReponseBody@RequestMapping("/books")publicclassBookController{//@RequestMapping(method = RequestMethod.POST)@PostMappingpublicString save(@RequestBodyBook book){? ? ? ? System.out.println("book save..."+ book);return"{'module':'book save'}";? ? }//@RequestMapping(value = "/{id}",method = RequestMethod.DELETE)@DeleteMapping("/{id}")publicString delete(@PathVariableInteger id){? ? ? ? System.out.println("book delete..."+ id);return"{'module':'book delete'}";? ? }//@RequestMapping(method = RequestMethod.PUT)@PutMappingpublicString update(@RequestBodyBook book){? ? ? ? System.out.println("book update..."+ book);return"{'module':'book update'}";? ? }//@RequestMapping(value = "/{id}",method = RequestMethod.GET)@GetMapping("/{id}")publicString getById(@PathVariableInteger id){? ? ? ? System.out.println("book getById..."+ id);return"{'module':'book getById'}";? ? }//@RequestMapping(method = RequestMethod.GET)@GetMappingpublicString getAll(){? ? ? ? System.out.println("book getAll...");return"{'module':'book getAll'}";? ? }? ? }
對于剛才的問題,我們都有對應(yīng)的解決方案:
每個方法的@RequestMapping注解中都定義了訪問路徑/books诡右,重復(fù)性太高安岂。
將@RequestMapping提到類上面,用來定義所有方法共同的訪問路徑帆吻。
每個方法的@RequestMapping注解中都要使用method屬性定義請求方式域那,重復(fù)性太高。
使用@GetMapping@PostMapping@PutMapping@DeleteMapping代替
每個方法響應(yīng)json都需要加上@ResponseBody注解桅锄,重復(fù)性太高琉雳。
1.將ResponseBody提到類上面样眠,讓所有的方法都有@ResponseBody的功能2.使用@RestController注解替換@Controller與@ResponseBody注解友瘤,簡化書寫
知識點1:@RestController
名稱@RestController
類型類注解
位置基于SpringMVC的RESTful開發(fā)控制器類定義上方
作用設(shè)置當(dāng)前控制器類為RESTful風(fēng)格,?等同于@Controller與@ResponseBody兩個注解組合功能
知識點2:@GetMapping @PostMapping @PutMapping @DeleteMapping
名稱@GetMapping @PostMapping @PutMapping @DeleteMapping
類型方法注解
位置基于SpringMVC的RESTful開發(fā)控制器方法定義上方
作用設(shè)置當(dāng)前控制器方法請求訪問路徑與請求動作檐束,每種對應(yīng)一個請求動作辫秧,?例如@GetMapping對應(yīng)GET請求
相關(guān)屬性value(默認(rèn)):請求訪問路徑