1. 程式人生 > 實用技巧 >spring-boot-learning-REST風格網站

spring-boot-learning-REST風格網站

什麼是REST風格:

Representational State Transfer :表現層狀態轉換,實際上是一種風格。標準,約定

首先需要有資源才能表現, 所以第一個名詞是“ 資源”。有了資源也要根
據需要以合適的形式表現資源,這就是第二個名詞一一表現層。最後是資源可以被新增、修改、刪
除等,也就是第三個名詞“狀態轉換”。

資源: 它可以是系統許可權使用者、角色和選單等,也可以是一些媒體型別, 如文字、圖片、歌曲,總之它就是一個具體存在的
物件。可以用一個URI ( Unifonn Resource Identifier ,統一資源定位符)指向它, 每個資源對應一個特定的U陽。
要獲取這個資源, 訪問它的U陽即可,而在REST 中每一個資源都會對應一個獨一無二的U阻。在阻ST 中, URI 也可以稱
為端點(End Point ) 。

表現層: 有了資源還需要確定如何表現這個資源。例如, 一個使用者可以使用JSON 、XML 或者其他的形式表現出來,又如
可能返回的是一幅圖片。在現今的網際網路開發中, JSON 資料集己經是一種最常用的表現形式

REST風格當中,每一個資源都只是對應一個網址,而一個資源網址應該是一個名詞,不存在動詞。

URI (Unifonn Resource Identifier)統一資源定位符

REST風格其實就是一種約定問題,不同的http請求對應的不同的資源操作,

@GetMapping

/**
 * RestController,作用是使返回的結果能夠以json資料集的方法進行返回
 * 轉換為JSTL或者json
 * 11預設將方法或者類標註為application/json;charset=UTF-8
 * 22方法執行結束後,spring會遍歷註冊號的HttpMessageConverter介面
 * 的實現類。
 * 33註冊好的MappingJackson2HttpMessageConverter就會放回true,
 *      啟動轉換器將結果轉換為JSON資料集
 */
@RestController
@RequestMapping(
"annotation") public class RedisUserController { @Autowired private UserService userService =null; /** * http的get請求,獲取資源 * @param id * @return */ @GetMapping("/{id}") public User getUser(@PathVariable("id") Long id){ System.out.println(System.currentTimeMillis()); User user = userService.getUser(id); System.out.println(System.currentTimeMillis()); return user; }

請求:

@PostMapping

   /**
     * http的post請求,建立資源
     * @param userName
     * @param note
     * @return
     */
    @PostMapping()
    public User insertUser(
            @RequestParam ("userName") String userName,
            @RequestParam ("note") String note
    ){
        User user = new User();
        user.setUserName(userName);
        user.setNote(note);
        userService.insertUser(user);
        return user;
    }

請求結果;

@DeleteMapping

 /**
     * http的Delete請求,刪除伺服器資源
     * @param id
     * @return
     */
    @DeleteMapping("{id}")
    public int delUser(@PathVariable("id") Long id){
        return userService.deleteUser(id);
    }

從資料庫中刪除了id為44員工

@PutMapping

@PatchMapping

  /**
     * http Put請求,提交所有的資源屬性以修改資源,
     * http patch請求,提交資源的部分修改屬性,
     * 其實他們兩個都差不多,只不過是約定的問題而已
     * @param id
     * @param userName
     * @return
     */
    @PatchMapping("/user/{id}")
    public User updUser(
            @PathVariable("id") Long id,
            @RequestParam("userName") String userName
    ){
       return userService.updateUser(id,userName);
    }

修改之前的:

處理HTTP請求狀態碼,異常和響應頭

通過實體類去實現;

/**
 * 當發生資源找不到或者處理邏輯發生異常時,需要考慮返回給客戶端的http狀態碼和錯誤訊息
 * spring提供實體封裝類ResponseEntity:有效的封裝錯誤訊息和狀態碼
 * 和註解@ResponseStatus:配置指定的響應碼給客戶端
 */
@RestController
public class HttpStatusController {
    @Autowired
    UserService userService;


    /**
     * 11新建一個請求頭物件,
     * 22向請求頭通過add方法進行加入k-v
     * 33需要建立一個響應實體類,將需要放回的資訊,請求頭物件,響應http狀態碼
     * 備註:這裡使用 HttpStatus.CREATED ,指定狀態碼為201,標識資源建立成功
     * @param userName
     * @param note
     * @return
     */
    @PostMapping("/http/status")
    public ResponseEntity<Integer> insertUser(
            @RequestParam("userName") String userName,
            @RequestParam ("note") String note
    ){
        User user = new User();
        user.setUserName(userName);
        user.setNote(note);
        Integer re = userService.insertUser(user);
//        設定http響應頭
        HttpHeaders httpHeaders = new HttpHeaders();//新建請求頭
        String success = (re == 0 ) ? "false" : "true";//如果結果為0,就是false
        httpHeaders.add("success",success);


        return new ResponseEntity<Integer>(re,httpHeaders, HttpStatus.CREATED);
    }

使用註解:

  /**
     * 通過註解@ResponseStatus去指定
     * 當方法正常返回的時候,http狀態碼為201
     * @param userName
     * @param note
     * @return
     */
    @PostMapping("/http/status/an")
    @ResponseStatus(HttpStatus.CREATED)
    public Integer insertUseran(
            @RequestParam("userName") String userName,
            @RequestParam ("note") String note
    ){
        User user = new User();
        user.setUserName(userName);
        user.setNote(note);
        Integer re = userService.insertUser(user);

        return  re;
    }
}

異常處理;

定義一個執行時的異常:

//場景:執行的時候,查詢id使用者,找不到資料或出現異常,這時候不能以正常返回去處理。
/**
 * 11自定義異常類:找不到使用者的時候丟擲異常
 * 異常丟擲後,可以在控制器通知@ControllerAdvice裡面進行處理
 * @ControllerAdvice:定義控制器通知
 * @ExceptionHandler:指定異常發生的處理方法
 *繼承的異常RuntimeException是指:執行時的異常,和我們普通的
 * Exception:受檢查的異常,這種異常是強制我們catch或throw的異常。
 *    你遇到這種異常必須進行catch或throw,如果不處理,編譯器會報錯。
 *
 */
public class NotFoundException extends RuntimeException{
    public static final long serialVersionUID = 1L;

    private Long code;//異常編碼
    private String customMsg;//異常自定義訊息

    public NotFoundException() {
    }

    public NotFoundException(Long code, String customMsg) {
        super();
        this.code = code;
        this.customMsg = customMsg;
    }

    public Long getCode() {
        return code;
    }

    public void setCode(Long code) {
        this.code = code;
    }

    public String getCustomMsg() {
        return customMsg;
    }

    public void setCustomMsg(String customMsg) {
        this.customMsg = customMsg;
    }
}

定義一個控制器通知:

/**
 * 2自定義一個控制器通知
 *      在@ControllerAdvice中指定攔截包路徑,限定被攔截的註解為@Controller/@RestController
 *      在@ExceptionHandler註解方法上,通過value屬性,指定異常型別進行攔截
 *      在@ResponseBody定義響應的資訊已json的格式表達
 *      在@ResponseStatus定義伺服器內部錯誤500程式碼
 */
@ControllerAdvice(
        basePackages = {"com.quan.annotationredis.controller.*"},
        annotations = {Controller.class, RestController.class}
)
public class UserControllerAdvice {
    @ExceptionHandler(value = NotFoundException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)//定義伺服器錯誤程式碼
    @ResponseBody
    public Map<String,Object> exception(HttpServletRequest request,
                                        NotFoundException ex){
        Map<String,Object> msgMap = new HashMap<>();
        msgMap.put("code",ex.getCode());
        msgMap.put("message",ex.getCustomMsg());
        return msgMap;
    }
}

測試controller:

@Controller
public class ExceptionController {

    @Autowired
    UserService userService;

    /**
     * 一旦方法出現異常,就會別控制器通知所攔截,最後經@ExceptionHandler定義
     * 的方法進行處理。
     * @param id
     * @return
     */
    @GetMapping("/exception/{id}")
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public User getUser(@PathVariable("id") Long id){
        System.out.println(System.currentTimeMillis());
        User user = userService.getUser(id);
        if (user == null){
            throw new NotFoundException(1L,"找不到使用者"+id+"的資訊");
        }
        System.out.println(System.currentTimeMillis());
        return user;
    }
}

使用RestTemplate請求後端;

底層是通過類HttpURLConnection實現的。

    /**
     * restTemplate.getForObject方法中:
     * 第一個引數URL:標明請求伺服器什麼資源,{id}代表引數
     * 第二個引數:表示將請求返回User類的結果,實際上伺服器只會給回我們json格式資料
     *          是因為restTemplate內部將其轉化給java物件。
     *  第三個引數:就是URL對應的引數。
     * @return
     */
    private static  User getUser() {
        int id = 1;
        RestTemplate restTemplate = new RestTemplate();
        User user = restTemplate.getForObject(
                "http://localhost:8012/annotation/{id}",
                User.class, id
        );
        System.out.println(user.getUserName());
        return user;
    }
}

輸出的日誌結果:

14:10:42.839 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP GET http://localhost:8012/annotation/1
14:10:42.904 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json]
14:10:42.921 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
14:10:42.924 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [com.quan.annotationredis.entity.User]
gangganghao

多個引數的時候:

@RestController
@RequestMapping("annotation")
public class RedisUserController {
    @Autowired
    private UserService userService =null;

    @PostMapping("/{userName}/{note}")
    public User insertUser(
            @PathVariable ("userName") String userName,
            @PathVariable ("note") String note
    ){
        User user = new User();
        user.setUserName(userName);
        user.setNote(note);
        userService.insertUser(user);
        return user;
    }

將引數用一個Map物件封裝起來,Map的鍵名稱和URI中所定義的引數時保持一致的。這樣子就能將引數統一封裝到Map中了

    /**
     *因為我們的Controller層返回的是User型別,所以我們這裡也放回User,並使用ResponseEntity<User>接受
     * postForEntity ,通過post請求返回一個實體類,需要url,請求型別,返回型別,引數列表
     * responseEntity.getBody();通過類的方法從返回體裡面拿到返回體的內容。(contorller裡面是返回user)
     * @return
     */
    public static User  insertUser(){
        RestTemplate restTemplate = new RestTemplate();
        String userName = "huolalala";
        String note = "huolalanote";
        String url = "http://localhost:8012/annotation/{userName}/{note}";

        Map<String,Object> params = new HashMap<>();
        params.put("userName",userName);
        params.put("note",note);

        ResponseEntity<User> responseEntity = restTemplate.postForEntity(url,User.class,User.class,params);
        User user = responseEntity.getBody();
        System.out.println(user);
        return user;
    }

執行日誌;

14:55:47.888 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP POST http://localhost:8012/annotation/huolalala/huolalanote
14:55:47.948 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json]
14:55:47.966 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [class com.quan.annotationredis.entity.User] with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
14:55:47.987 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK
14:55:47.990 [main] DEBUG org.springframework.web.client.RestTemplate - Reading to [com.quan.annotationredis.entity.User]
User{id=49, userName='huolalala', note='huolalanote'}

請求體獲取引數:

  /**
     * 11先定義請求頭HttpHeaders,設定請求體為JSON格式
     * 22將請求體實體user和請求頭繫結到請求實體物件HttpEntiry
     * 33restTemplate.postForObject將請求物件傳遞過去,
     * @return
     */
    public static User insertUser1(){
        User user = new User();
        user.setNote("RRRR");
        user.setUserName("QQQQ");

        HttpHeaders httpheaders = new HttpHeaders();
        httpheaders.setContentType(MediaType.APPLICATION_JSON_UTF8);

        HttpEntity<User> httpEntity = new HttpEntity<>(user,httpheaders);

        RestTemplate restTemplate = new RestTemplate();
        restTemplate.postForObject("http://localhost:8012/annotation",httpEntity,User.class);

        return user;

    }

刪除:

    /**
     *
     */
    public static void delUser(){
        Long id = 48L;
        RestTemplate restTemplate = new RestTemplate();
        String url = "http://localhost:8012/annotation/{id}";
        restTemplate.delete(url,id);//這個方法沒有返回值
    }

執行日誌:

17:09:06.618 [main] DEBUG org.springframework.web.client.RestTemplate - HTTP DELETE http://localhost:8012/annotation/48
17:09:06.639 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK