REST(Representational State Transfer),中文翻譯叫「表述性狀態轉移」。是 <span style="color:blue;font-size:15px;font-family:Microsoft YaHei;font-style:oblique;">Roy Thomas Fielding</span> 在他2000年的博士論文中提出的。它與傳統的 SOAP Web 服務區別在於,REST關注的是要處理的數據,而 SOAP 主要關注行爲和處理。要理解好 REST,根據其首字母拆分出的英文更容易理解。 **表述性(Representational):**對於 REST 來講,咱們網絡上的一個個URI資源能夠用各類形式來表述,例如:XML、JSON或者HTML等。 **狀態(State):**REST 更關注資源的狀態而不是對資源採起的行爲。 **轉移(Transfer):**在網絡傳輸過程當中,REST 使資源以某種表述性形式從一個應用轉移到另外一個應用(如從服務端轉移到客戶端)。html
具體來講,REST 中存在行爲,它的行爲是經過 HTTP 表示操做的方法來定義的即:GET、POST、PUT、DELETE、PATCH;GET用來獲取資源,POST用來新建資源(也能夠用於更新資源),PUT用來更新資源,DELETE用來刪除資源,PATCH用來更新資源。 基於 REST 這樣的觀點,咱們須要避免使用 REST服務、REST Web服務 這樣的稱呼,這些稱呼多少都帶有一些強調行爲的味道。java
**RESTful 架構:**是基於 REST 思想的時下比較流行的一種互聯網軟件架構。它結構清晰、符合標準、易於理解、擴展方便,因此正獲得愈來愈多網站的採用。git
在 spring 3.0 之後,spring 這對 springMVC 的一些加強功能對 RESTful 提供了良好的支持。在4.0後的版本中,spring 支持一下方式建立 REST 資源:github
代碼清單web
package com.pengdh.controller; import com.pengdh.entity.EmployeeEntity; import com.pengdh.service.EmployeeService; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * @author pengdh * @date: 2017-06-27 0:08 */ @Controller @RequestMapping("/employs") public class EmployeeController { @Autowired private EmployeeService empService; @RequestMapping(value = "/list", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" }) public List<EmployeeEntity> employs(Integer offset,Integer limit) { offset = offset == null ? 0 : offset; limit = limit == null ? 20 : limit; return empService.queryEmployList(offset,limit); } }
代碼的大體過程是當客戶端發起對 "/employs" 的 GET 請求時,將調用服務端的 employs 方法,服務端經過注入的 EmployeeService 獲取到一個 EmployeeEntity 列表,並將列表以 JSON 的表述形式返回給客戶端。spring
對於上述兩種方式,第一種方式是經過 ContentNegotiatingViewResolver 做爲 ViewResolver 的實現,主要是用於將資源渲染人類用戶接口所須要的視圖模型,如:HTML、JSP等也能夠渲染。也能夠針對不是人類客戶端產生 JSON 或 XML,可是效果不是很理想,每每會產生一些不是客戶端所須要的預期結果。如:客戶端但願獲得的響應多是:{"name":"zhangs","age":"20"}。而模型是 key-value 組成的 map ,可能最終的響應是這樣的:{"user":{"name":"zhangs","age":"20"}}。基於內容協商的這些限制,這裏咱們主要討論第二種方式:使用 Spring 的消息轉換功能來生成資源表述。json
這是一種更爲直接的方式,消息轉換器可以將控制器產生的數據轉換爲服務於客戶端的表述形式。經常使用的一些消息轉換器如:Jackson 的 MappingJacksonHttpMessageConverter 實現 JSON 消息和 Java 對象的互相轉換; JAXB 庫的 Jaxb2RootElementHttpMessageConverter 實現 XML 和 Java 對象的相互轉換等。api
正常狀況下,當處理方法返回 Java 對象時,這個對象會放在模型中並在視圖中渲染使用。可是,若是使用了消息轉換功能的話,咱們須要告訴 Spring 跳過正常的模型/視圖流程,並使用消息轉換器。實現這種方式最簡單的方式是在控制器的方法上添加 @ResponseBody 註解。如:restful
@RequestMapping(value = "/list", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" }) @ResponseBody public List<EmployeeEntity> employs(Integer offset,Integer limit) { offset = offset == null ? 0 : offset; limit = limit == null ? 20 : limit; return empService.queryEmployList(offset,limit); }
這裏 @ResponseBody 註解會告知 Spring 將 List<EmployeeEntity> 轉換成 JSON 這樣的表述形式做爲資源發送給客戶端。網絡
使用 @RequestBody 註解能夠告知 Spring 查找一個消息轉換器,未來自客戶端的資源表述轉換爲對象。如:
@RequestMapping(value = "/save", method = RequestMethod.POST, produces = { "application/json;charset=UTF-8" }) public int saveEmploy(@RequestBody EmployeeEntity employeeEntity) { return empService.save(employeeEntity); }
Spring 4.0 引入了 @RestController 註解,在控制器是用 @RestController 代替 @Controller 的話,Spring 將會爲該控制器的全部處理方法應用消息轉換功能。咱們沒必要在每一個方法都添加 @ResponseBody 註解了。如:
package com.pengdh.controller; import com.pengdh.entity.EmployeeEntity; import com.pengdh.service.EmployeeService; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** * @author pengdh * @date: 2017-06-27 0:08 */ @RestController @RequestMapping("/employs") public class EmployeeController { @Autowired private EmployeeService empService; @RequestMapping(value = "/list", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" }) public List<EmployeeEntity> employs(Integer offset,Integer limit) { offset = offset == null ? 0 : offset; limit = limit == null ? 20 : limit; return empService.queryEmployList(offset,limit); } @RequestMapping(value = "/save", method = RequestMethod.POST, produces = { "application/json;charset=UTF-8" }) public int saveEmploy(@RequestBody EmployeeEntity employeeEntity) { return empService.save(employeeEntity); } }
能夠利用 ResponseEntity 給客戶端返回狀態碼、設置響應頭信息等,如給客戶端提供返回碼:
@RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" }) public ResponseEntity<EmployeeEntity> employById(@PathVariable long id) { HttpStatus status = null; EmployeeEntity employeeEntity = empService.selectById(id); if (employeeEntity != null) { status = HttpStatus.OK; } else { status = HttpStatus.NOT_FOUND; } return new ResponseEntity<EmployeeEntity>(employeeEntity, status); }
若是沒有 if 判斷,當根據 id 找不到對應的信息的時候,返回給客戶端的狀態碼是默認的 HttpStatus.OK;當加上了判斷條件後若是沒有相應的信息返回則設置返回狀態碼爲 HttpStatus.NOT_FOUND,最後經過 new 一個 ResponseEntity 會將查詢信息和狀態碼一塊兒返回到客戶端。
@ExceptionHandler 能夠用到控制器的方法中,處理特定的異常:
建立響應包裝類 ResponseResult
package com.pengdh.dto; import java.io.Serializable; import org.springframework.http.HttpStatus; /** * 響應結果封裝類 * * @author pengdh * @date: 2017-06-29 0:34 */ public class ResponseResult<T> implements Serializable { private static final long serialVersionUID = -3371934618173052904L; private int code; private String desc; private T data; public ResponseResult() { } public ResponseResult(int code, String desc) { this.code = code; this.desc = desc; } public ResponseResult(int code, T data) { this.code = code; this.data = data; } public int getCode() { return code; } public void setCode(HttpStatus code) { this.code = code; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public String toString() { return "ResponseResult{" + "code=" + code + ", desc='" + desc + '\'' + ", data=" + data + '}'; } }
建立一個異常類 ResourceNotFound
package com.pengdh.exception; /** * 資源未找到異常 * * @author pengdh * @date: 2017-06-29 0:55 */ public class ResourceNotFound extends RuntimeException { private static final long serialVersionUID = 4880328265878141724L; public ResourceNotFound() { super(); } public ResourceNotFound(String message) { super(message); } public ResourceNotFound(String message, Throwable cause) { super(message, cause); } }
控制器 EmployeeController
package com.pengdh.controller; import com.pengdh.dto.ResponseResult; import com.pengdh.entity.EmployeeEntity; import com.pengdh.exception.ResourceNotFound; import com.pengdh.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** * @author pengdh * @date: 2017-06-27 0:08 */ @RestController @RequestMapping("/employs") public class EmployeeController { @Autowired private EmployeeService empService; @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" }) public ResponseResult<EmployeeEntity> employById(@PathVariable long id) { ResponseResult<EmployeeEntity> result = new ResponseResult<EmployeeEntity>(); HttpStatus status = null; EmployeeEntity employeeEntity = empService.selectById(id); if (employeeEntity == null) { throw new ResourceNotFound(String.valueOf(id)); } result.setCode(HttpStatus.OK); result.setData(employeeEntity); return result; } @ExceptionHandler(ResourceNotFound.class) public ResponseResult<Object> handlerException(ResourceNotFound e) { ResponseResult<Object> result = new ResponseResult<Object>(); result.setCode(HttpStatus.NOT_FOUND); result.setDesc(e.getMessage()); return result; } }
從控制器代碼能夠看出,咱們經過 @ExceptionHandler 能將控制器的方法的異常場景分出來單獨處理。