深入淺出boot2.0 第11章 rest風格,restTemplae

  • rest風格:被推薦各個微服務系統之間用於交互的方式
  • 每一個資源對應着一個網站
  • 資源網址 是一個名詞,而不是動詞
  • 簡易參數通過網址 進行傳遞
  • http://localhost:8080/user/1
    • user名詞,代表用戶信息
    • 1 是用戶編號,獲取用戶id爲1的資源

rest概述

  • roy thomas fielding在 2000年 博士論文提出的

  • fielding是 http 1.0 和 1.1的主要設計者,apache 服務器軟件的作者,apache 第一任主席

  • 將他對 互聯網 軟件的架構原則,命名爲:rest (representationnal state transfer)

  • representationnal state transfer 表現狀態轉換

  • 首先需要有資源才能表現 (資源)

  • 有了資源也要根據需要以 合適的形式 表現資源 (表現層)

  • 資源可以 新增 修改 刪除 (狀態轉換)

  • 資源:

    • 系統權限用戶 角色 菜單
    • 媒體類型 文本 圖片 歌曲
    • 具體存在的對象,可以用URL,統一資源定位器 指向它
    • 每一個資源 都會 對應一個 獨一無二的URI,URI 可以稱爲端點 end Point
  • 表現層:

    • 如何表現這個資源, json xml 一幅圖片
  • 狀態轉換:

    • 資源並不是 一成不變的,資源經歷:創建,訪問,修改,刪除
    • http協議,是一個沒有狀態的協議,只能在服務端保存,http中存在多種動作來對應這些變化。
  • result風格的特點

    • 每個資源通過 單獨唯一的 URI 進行標識
    • 客戶端和服務端相互傳遞資源,而資源以某種表現層 得以展示
    • 客戶端通過 HTTP協議所定義的動作 對資源進行操作。

http的動作

  • rest風格 通過http的行爲去操作資源

  • 創建 修改 訪問 刪除 轉換

    • get (visit) 訪問
    • post (create) 用來創建新的資源
    • put (update) 修改,需要把資源的所有屬性,一併提交
    • patch (update) 修改已經存在的資源,只需要將部分資源屬性提交。(這個動作並不普及,java還不能完全支持它)
    • delete 從服務器將資源刪除
  • 對於 http協議 ,還有另外兩種不常用的動作行爲

    • head 獲取資源的元數據 content-type
    • options 提供資源 可供 客戶端修改的屬性信息
    • 實用價值不大
  • rest風格的URI設計

    • get /user/1
  • get /users/{username}/{note}

    • post /user/{username}/{sex}/{note}
  • put /user/{id}/{username}/{sex}/{note}

    • patch /user/{id}/{userName} 修改局部
  • URI 不能出現動詞,對於參數 主要通過 URI設計去獲取

    • 對於參數 超過 5個 ,用 JSON 傳遞。(我覺得超過4個就用json)

rest風格誤區

  • 不應該出現動詞。 get /user/get/1 修改爲 /user/1
  • 不應該有版本號。 get /v1/user/1 。應該 設置http請求頭, Accept:version=1.0
  • put users?userName=user_name&note=note。建議 /users/{userName}/{note} 參數比較多用 json

spring MVC 開發 rest風格端點

  • 在 4.3 之後,更多的註解 引入 使得 REST 風格的開發 更爲便捷

  • @RequestMapping

  • @GetMapping

  • @PostMapping

  • @PutMapping 提交所有資源

  • @PatchMapping 提交部分資源

  • @DeleteMapping

  • 簡單參數,通過URL直接傳遞

  • @PathVariable

  • @RequestBody 多個用json

    • MVC 通過, MappingJackson2HttpMessageConverter
  • @RestConteroller 整個控制器 都默認轉換爲 json數據

  • 請求一幅圖片,一段視頻。 Spring提供 協商資源的視圖解析器 ContentNegotiatingViewResolver

使用 Spring開發 rest風格的端點

  • dao使用的是 PO (persisent Object),直接對應數據庫的表

    • @Alias("user")
      public class User {
      	private Long id;
      	private String userName;
      	private String note;
          private SexEnum sex=null;
      }
      
    • po sex是枚舉類型的

  • 前端難以理解,所以有了 VO

    • public class User {
      	private Long id;
      	private String userName;
      	private String note;
          
          private String sexName;
          private int sexCode;
          
      }
      
    • 把枚舉類轉換成 字符串 和 代碼

通用工具類

	public enum SexEnum {
	
	MALE(0, "男"),
	FEMALE(1, "女");
	
	private int code;
	private String name;
			
	SexEnum(int code, String name) {
		this.code = code;
		this.name = name;
	}
	
	public static SexEnum getSexEnum(int code) {
		for (SexEnum sex : SexEnum.values()) {
			if (sex.getCode() == code) {
				return sex;
			}
		}
		return null;
	}
}


	// 轉換Vo變爲PO
	private User changeToPo(UserVo userVo) {
		User user = new User();
		user.setId(userVo.getId());
		user.setUserName(userVo.getUserName());
		user.setSex(SexEnum.getSexEnum(userVo.getSexCode()));
		user.setNote(userVo.getNote());
		return user;
	}

	// 轉換PO變爲VO
	private UserVo changeToVo(User user) {
		UserVo userVo = new UserVo();
		userVo.setId(user.getId());
		userVo.setUserName(user.getUserName());
		userVo.setSexCode(user.getSex().getCode());
		userVo.setSexName(user.getSex().getName());
		userVo.setNote(user.getNote());
		return userVo;
	}

	// 將PO列表轉換爲VO列表
	private List<UserVo> changeToVoes(List<User> poList) {
		List<UserVo> voList = new ArrayList<>();
		for (User user : poList) {
			UserVo userVo = changeToVo(user);
			voList.add(userVo);
		}
		return voList;
	}


	// 結果VO
	public class ResultVo {
		private Boolean success = null;
		private String message = null;

		public ResultVo() {
		}
		public ResultVo(Boolean success, String message) {
			this.success = success;
			this.message = message;
		}
	}

插入 controller 和 js

@Controller
public class UserController {
	// 用戶服務接口
	@Autowired
	private UserService userService = null;

	// 映射JSP視圖
	@GetMapping("/restful")
	public String index() {
		return "restful";
	}
	
	@PostMapping("/user")
	@ResponseBody
	public User insertUser(@RequestBody UserVo userVo) {
	    User user = this.changeToPo(userVo);
	    return userService.insertUser(user);
	}
    
}
	 function post() {
         var params = {
         'userName': 'user_name_new', 
         'sexCode' : 1,
         'note' : "note_new"
	 }


$.post({
	 url : "./user",
	 // 此處需要告知傳遞參數類型爲JSON,不能缺少
	 contentType : "application/json",
	 // 將JSON轉化爲字符串傳遞
	 data : JSON.stringify(params),
	 // 成功後的方法
	 success : function(result) {
         if (result == null || result.id == null) {
             alert("插入失敗");
             return;
             }
             alert("插入成功");
             }
         });
	 }

得到 get和 js

	// 獲取用戶
	@GetMapping(value = "/user/{id}")
	@ResponseBody
	public UserVo getUser(@PathVariable("id") Long id) {
	    User user = userService.getUser(id);
	    return changeToVo(user);
	}

	@GetMapping("/users/{userName}/{note}/{start}/{limit}")
	@ResponseBody
	public List<UserVo> findUsers(
	        @PathVariable("userName") String userName, 
	        @PathVariable("note") String note, 
	        @PathVariable("start") int start, 
	        @PathVariable("limit") int limit) {
	    List<User> userList = userService.findUsers(userName, note, start, limit);
	    return this.changeToVoes(userList);
	}//如果參數多於 5個,就用json傳遞

	 function get() {
	 $.get("./user/1", function(user, status) {
         if (user == null) {
         alert("結果爲空")
         } else {
         alert("用戶信息爲"+JSON.stringify(user));
         }
	 });
	 }
	 get();

	 function findUsers() {
	 $.get("./users/u/n/0/5", function(user, status) {
	 if (user == null) {
	 alert("結果爲空")
	 } else {
	 alert("用戶信息爲:"+JSON.stringify(user));
	 }
	 });
	 }
	 findUsers();

修改

	@PutMapping("/user/{id}")
	@ResponseBody
	public User updateUser(@PathVariable("id") Long id, @RequestBody UserVo userVo) {
	    User user = this.changeToPo(userVo);
	    user.setId(id);
	    userService.updateUser(user);
	    return user;
	}


	function updateUser() {
		var params = {
			'userName' : 'user_name_1_update',
			'sexCode' : 1,
			'note' : "note_new_1"
		}
		$.ajax({
			url : "./user/1",
			// 此處告知使用PUT請求
			type : 'PUT',
			// 此處需要告知傳遞參數類型爲JSON,不能缺少
			contentType : "application/json",
			// 將JSON轉化爲字符串傳遞
			data : JSON.stringify(params),
			success : function(user, status) {
				if (user == null) {
					alert("結果爲空")
				} else {
					alert(JSON.stringify(user));
				}
			}
		});
	}

	updateUser();
//jquery 不存在put,用 ajax替代

修改局部

	//一些重要的java類 可能還不能夠支持它
function updateUserName() {
	    $.ajax({url:"./user/1/user_name_patch", 
	        type:"PATCH", 
	        success: function(result, status) {
	            if (result == null) {
	                   alert("結果爲空")
	            } else {
	                alert(result.success? "更新成功" : "更新失敗");
	            }
	        }
	    })
	}
	updateUserName();
	@PatchMapping("/user/{id}/{userName}")
	@ResponseBody
	public ResultVo changeUserName(@PathVariable("id") Long id, 
	        @PathVariable("userName") String userName) {
	    int result = userService.updateUserName(id, userName);
	    ResultVo resultVo = new ResultVo(result>0, 
	        result > 0 ? "更新成功" : "更新用戶【" + id + "】失敗。");
	    return resultVo;
	}

刪除

	@DeleteMapping("/user/{id}")
	@ResponseBody
	public ResultVo deleteUser(@PathVariable("id") Long id) {
	    int result = userService.deleteUser(id);
	    ResultVo resultVo = new ResultVo(result>0, 
	        result > 0 ? "更新成功" : "更新用戶【" + id + "】失敗。");
	    return resultVo;
	}
	function deleteUser() {
	    $.ajax({
	        url : "./user/1", 
	        type :'DELETE', 
	        success : function(result) {
	        if (result == null) {
	            alert("結果爲空")
	        } else {
	            alert(result.success? "刪除成功" : "刪除失敗");
	        }
	    }});
	}
	deleteUser();

表單提交修改

	@PatchMapping("/user/name")
	@ResponseBody
	public ResultVo changeUserName2(Long id, String userName) {
	    int result = userService.updateUserName(id, userName);
	    ResultVo resultVo = new ResultVo(result>0, 
	        result > 0 ? "更新成功" : "更新用戶名【" + id + "】失敗。");
	    return resultVo;
	}

	// 映射JSP視圖
	@GetMapping("/user/name")
	public String changeUserName() {
	    return "change_user_name";
	}
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>表單定義HTTP動作</title>
</head>
    <body>
        <form id="form" action="./name" method="post">
            <table>
                <tr>
                    <td>用戶編號</td>
                    <td><input id="id" name="id" /></td>
                </tr>
                <tr>
                    <td>用戶名稱</td>
                    <td><input id="userName" name="userName" /></td>
                </tr>
                <tr>
                    <td></td>
                    <td align="right"><input id="submit" name="submit"
                        type="submit" /></td>
                </tr>
            </table>
            <input type="hidden" name="_method" id="_method" value="PATCH" />
        </form>
    </body>
</html>

1.form 定義爲post
2.form 中還存在 一個命令爲 _method 。
這樣就能定位到了

@RestController

@RestController
public class UserController2 {

	// 用戶服務接口
	@Autowired
	private UserService userService = null;

	// 映射JSP視圖
	@GetMapping(value = "/restful2")
	public ModelAndView index() {
		ModelAndView mv = new ModelAndView("restful");
		return mv;
	}

	// 獲取用戶
	@GetMapping(value = "/user2/{id}")
	public UserVo getUser(@PathVariable("id") Long id) {
		User user = userService.getUser(id);
		return changeToVo(user);
	}
    
}
  • JSP 也要渲染的話,就不能直接返回字符串了

渲染結果

  • 還有 PDF,Excel 等媒體類型 MediaType

    • 一種:@ResponseBody,用Ioc容器中 HttpMessageConverter 接口實現類的 MappingJackson2HttpMessageConverter

    • 一種:ModelAndView mv = new ModelAndView(“restful”); 使用ModelAndView捆綁視圖

      • HttpMessageConverter 接口有兩個實現類:StringHttpMessageConverter 和 MappingJackson2HttpMessageConverter

      • HttpMessageConverter 定義了:boolean canWrite(Class<?> var1, @Nullable MediaType var2); 。MediaType 是可以傳入的媒體類型。MVC在執行控制器方法後,會變量HttpMessageConverter 接口的實現類。並使用canWrite 判斷是否攔截控制器的返回

      • @RequestMapping GetMapping 還存在 consumes和produces

        • consumes 代表限制該方法 接收什麼類型的請求體 (body)。消費者。action是消費 前端的請求

        • produces 代表限定返回的媒體類型。 action是提供者。僅當 request請求頭中 (Accept ) 類型中包含 該指定類型才返回。

        • @RestController
          public class UserController2 {
          
          // 獲取用戶
          	@GetMapping(value = "/user2/{id}")
          	public UserVo getUser(@PathVariable("id") Long id) {
          		User user = userService.getUser(id);
          		return changeToVo(user);
          	}
          
          	@GetMapping(value = "/user2/name/{id}",
          			// 接受任意類型的請求體
          			consumes = MediaType.ALL_VALUE,
          			// 限定返回的媒體類型爲文本
          			produces = MediaType.TEXT_PLAIN_VALUE)
          	public String getUserName(@PathVariable("id") Long id) {
          		User user = userService.getUser(id);
          		// 返回字符
          		return user.getUserName();
          	}
              
          }
          
          	@GetMapping(value = "/user2/name/{id}",
          			// 接受任意類型的請求體
          			consumes = MediaType.ALL_VALUE,
          			// 限定返回的媒體類型爲文本
          			produces = MediaType.TEXT_PLAIN_VALUE)
          
        • @RestController 默認方法標註爲:application/json;charset=UTF-8

        • getUser方法結束後,遍歷 HttpMessageConverter 接口有兩個實現類:StringHttpMessageConverter 和 MappingJackson2HttpMessageConverter 。MappingJackson2HttpMessageConverter 的 canWrite方法 返回true

        • 就將啓用這個轉換器 將其裝換成 json 數據集

        • getUserName consumes爲接收所有的請求體 body。所以可接收任何的請求體。 結果聲明爲 了 普通文本(會修改默認的json類型)。會被 StringHttpMessageConverter 攔截。 轉爲一個簡單的字符串

  • 對於沒有 HttpMessageConverter 機制 沒有處理的數據模型,它會 流轉到視圖解析器(ViewResolver)。ModelAndView mv = new ModelAndView(“restful”); Spring對rest支持中,還提供 協商視圖解析器 contentNegotiatingViewResolver 。控制器找不到HttpMessageConverter ,就會流轉到他那裏。解析到這個view類,然後返回,找到視圖解析器 如: InternalResourceViewResolver ,對jsp渲染

  • 視圖解析器

    • BeanName ViewResolver 根據請求URI名稱找到 對應的視圖
    • View Resolver Composite 視圖解析器組合
    • InternalResourceView 邏輯視圖解析器,最常用。子類有 JstlViewResolver

處理 http 狀態碼,異常 和 響應頭

  • 沒有找到資源,或者發生異常。

  • 返回給前端 http狀態碼 和 錯誤消息

  • spring提供 實體類封裝類 ResponseEntiry 和 註解 @ResponseStatus

    • 封裝錯誤消息 和 狀態碼,通過 @ResponseStatus 配置指定的響應碼給 客戶端

    • 200 代表請求成功

    • 201 代表新增資源成功

    • http響應頭加入屬性 響應碼

  • @PostMapping(value = "/user2/entity")
        public ResponseEntity<UserVo> insertUserEntity(
                @RequestBody UserVo userVo) {
            User user = this.changeToPo(userVo);
            userService.insertUser(user);
            
            UserVo result = this.changeToVo(user);
            //定義hearder
            HttpHeaders headers = new HttpHeaders();
            
            String success = 
                (result == null || result.getId() == null) ? "false" : "true";
            // 設置響應頭,比較常用的方式
            headers.add("success", success);
            
            // 下面是使用集合(List)方式,不是太常用
            // headers.put("success", Arrays.asList(success));
            
            // 返回創建成功的狀態碼
            return new ResponseEntity<UserVo>(result, headers, HttpStatus.CREATED);
            
    }
    
    
        @PostMapping(value = "/user2/annotation")
        // 指定狀態碼爲201(資源已經創建)
        @ResponseStatus(HttpStatus.CREATED)
        public UserVo insertUserAnnotation(@RequestBody UserVo userVo) {
            User user = this.changeToPo(userVo);
            userService.insertUser(user);
            UserVo result = this.changeToVo(user);
            return result;
        }
    
    
    
  • function postStatus() {
    	    // 請求體
    	    var params = {
    	        'userName': 'user_name_new', 
    	        'sexCode' : 1,
    	        'note' : "note_new"
    	    }
    	    var url = "./user2/annotation";
    	    // var url = "./user2/annotation";
    	    $.post({
    	        url : url,
    	        // 此處需要告知傳遞參數類型爲JSON,不能缺少
    	        contentType : "application/json",
    	        // 將JSON轉化爲字符串傳遞
    	        data : JSON.stringify(params),
    	        // 成功後的方法
    	        success : function(result, status, jqXHR) {
    	            // 獲取響應頭
    	            var success = jqXHR.getResponseHeader("success");
    	            // 獲取狀態碼
    	            var status = jqXHR.status;
    	            alert("響應頭參數是:" + success+",狀態碼是:" + status);
    	            if (result == null || result.id == null) {
    	                alert("插入失敗");
    	                return;
    	            }
    	            alert("插入成功");
    	        }
    	    });
    	}
    
    
    {
        "id": 6,
        "userName": "我的測試2",
        "sexCode": 1,
        "sexName": "女",
        "note": "我的測試備註2"
    }
    上面兩個返回都一樣。 第一個header頭會返回success 爲true。status 都會返回201
    

異常處理

  • 異常可以用 @ControllerAdvice (定義控制器通知) 和 @ExceptionHandler (異常發生的處理方法)

異常處理類

  • public class NotFoundException extends RuntimeException {
    	private 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;
    	}
    }
    
  • 找不到用戶的時候,拋出此異常,在 控制器通知 @controller Advice中 來處理這些異常

  • 使用:@ExceptionHandler 。Boot 裏面自帶 BasicErrorController

異常處理的通知

//控制器通知
@ControllerAdvice(
		// 指定攔截包的控制器
		basePackages = { "com.springboot.chapter11.controller.*" },
		// 限定被標註爲@Controller或者@RestController的類才被攔截
		annotations = { Controller.class, RestController.class })
public class VoControllerAdvice {
	// 異常處理,可以定義異常類型進行攔截處理
	@ExceptionHandler(value = NotFoundException.class)
	// 以JSON表達方式響應
	@ResponseBody
	// 定義爲服務器錯誤狀態碼
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)//500
	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;
	}
}
{
    "code": 1,
    "message": "找不到用戶【1000】信息"
}

測試action

	@GetMapping(value="/user/exp/{id}",
	        // 產生JSON數據集
	        produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	// 響應成功
	@ResponseStatus(HttpStatus.OK)
	@ResponseBody
	public UserVo getUserForExp(@PathVariable("id") Long id) {
	    User user = userService.getUser(id);
	    // 如果找不到用戶,則拋出異常,進入控制器通知
	    if (user == null) {
	        throw new NotFoundException(1L, "找不到用戶【" + id +"】信息");
	    }
	    UserVo userVo = changeToVo(user);
	    return userVo;
	}

dao代碼

@Mapper
public interface UserDao {
	public User getUser(Long id);
	public int insertUser(User user);
    
	public List<User> findUsers(@Param("userName") String userName, @Param("note") String note, @Param("start") int start,  @Param("limit") int limit);
	
	public int updateUser(User user);
	
	public int updateUserName(@Param("id") Long id, @Param("userName") String userName);
	
	public int deleteUser(Long id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springboot.chapter11.dao.UserDao">
    <resultMap type="user" id="userMapper">
        <result column="id" property="id"/>
        <result column="user_name" property="userName"/>
        <result column="sex" property="sex" typeHandler="com.springboot.chapter11.typeHandler.SexTypeHandler"/>
        <result column="note" property="note"/>
    </resultMap>
    
	<select id="getUser" resultMap="userMapper">
		select id, user_name, sex, note from t_user where id = #{id}
	</select>
	
	<select id="findUsers" resultMap="userMapper">
	   select id, user_name, sex, note from t_user
	   <where>
	        <if test="userName != null"> and user_name like concat('%', #{userName}, '%')</if>
	        <if test="note != null"> and note like concat('%', #{note}, '%')</if>
	   </where>
	   limit #{start}, #{limit}
	</select>
	
	<insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
	    insert into t_user(user_name, sex, note) values(#{userName}, #{sex, typeHandler=com.springboot.chapter11.typeHandler.SexTypeHandler}, #{note})
	</insert>
	
	<update id="updateUser" parameterType="user">
	    update t_user 
	    <set>
	        <if test="userName != null"> user_name = #{userName},</if>
	        <if test="sex != null"> sex = #{sex, typeHandler=com.springboot.chapter11.typeHandler.SexTypeHandler},</if>
	        <if test="note != null">note = #{note}</if>
	    </set>
	    where id= #{id}
	</update>
	
	<update id="updateUserName">
	     update t_user set user_name = #{userName} where id = #{id}
	</update>
	
	<delete id="deleteUser" parameterType="long">
	    delete from t_user where id = #{id}
	</delete>
</mapper>

解析器

@MappedTypes(SexEnum.class)
@MappedJdbcTypes(JdbcType.INTEGER)
public class SexTypeHandler extends BaseTypeHandler<SexEnum> {

	@Override
	public SexEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
		int code = rs.getInt(columnName);
		return SexEnum.getSexEnum(code);
	}

	@Override
	public SexEnum getNullableResult(ResultSet rs, int index) throws SQLException {
		int code = rs.getInt(index);
		return SexEnum.getSexEnum(code);
	}

	@Override
	public SexEnum getNullableResult(CallableStatement cs, int index) throws SQLException {
		int code = cs.getInt(index);
		return SexEnum.getSexEnum(code);
	}

	@Override
	public void setNonNullParameter(PreparedStatement ps, int index, SexEnum sex, JdbcType jdbcType) throws SQLException {
		ps.setInt(index, sex.getCode());
	}

}

客戶端請求 TestTemplate

  • 每個微服務都會暴露 Rest風格的URI 請求,給別的微服務系統所調用

  • 爲了方便相互調用 給予了模板類 RestTemplate (還有聲明式調用)

  • 產品 用戶 財務 交易 分別作爲一個單獨的系統

  • 交易系統:希望得到 產品 ,用戶 ,和 財務的信息

  • 有 webService 和 遠程調用 (RPC)

  • 推薦使用 REST風格 來完成系統之間的交互

  • 會帶來併發的過程導致數據的不一致,分佈式數據庫事務

  • 在 Spring 5 所推出的 WebFlux 中,還有 WebClient

  • RestTemplate底層是: HttpURLConnection實現的

最簡單的查詢

	// 獲取用戶
	public static UserVo getUser(Long id) {
	    // 創建一個RestTemplate對象
	    RestTemplate restTmpl = new RestTemplate();
	    // 消費服務,第一個參數爲url,第二個是返回類型,第三個是URI路徑參數
	    UserVo userVo = restTmpl.getForObject(
	            "http://localhost:8080/user/{id}", UserVo.class, id);
	    // 打印用戶名稱
	    System.out.println(userVo.getUserName());
	    return userVo;
	}
  • 實際服務器只會返回Json類型的數據給我們,RestTemplate 會轉成java

多個參數,簡介寫法

public static List<UserVo> findUser(String userName, 
	        String note, int start, int limit) {
    
	    RestTemplate restTmpl = new RestTemplate();
    
	    // 使用Map進行封裝多個參數,以提高可讀性
	    Map<String, Object> params = new HashMap<String, Object>();
	    params.put("userName", "user");
	    params.put("note", "note");
	    params.put("start", start);
	    params.put("limit", limit);
    
	    // Map中的key和URI中的參數一一對應
	    String url = "http://localhost:8080/users/{userName}/{note}/{start}/{limit}"; 
    
	    // 請求後端
	    ResponseEntity<List> responseEntity = restTmpl.getForEntity(url, List.class, params);
    
	    List<UserVo> userVoes = responseEntity.getBody();
    
	    return userVoes;
	}
  • 先將map封裝起來,

json請求

	// 新增用戶
	public static User insertUser(UserVo newUserVo) {
	    // 請求頭
		HttpHeaders headers = new HttpHeaders();
	    // 設置請求內容爲JSON類型
		headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
	    // 創建請求實體對象
	    HttpEntity<UserVo> request = new HttpEntity<>(newUserVo, headers);
        
	    RestTemplate restTmpl = new RestTemplate();
	    // 請求時傳遞請求實體對象,並返回回填id的用戶
	    User user = restTmpl.postForObject("http://localhost:8080/user", request, User.class);
        
	    System.out.println(user.getId());
	    return user;
	}
	/**
	 * A String equivalent of {@link MediaType#APPLICATION_JSON_UTF8}.
	 *相當於{@link MediaType#APPLICATION_JSON_UTF8}的字符串。
	 
	 * @deprecated as of 5.2 in favor of {@link #APPLICATION_JSON_VALUE}
	 * @deprecated as 5.2贊成{@link #APPLICATION_JSON_VALUE}
	 
	 * since major browsers like Chrome
	 *主要瀏覽器如Chrome
	 * <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=438464">
	 
	 * now comply with the specification</a> and interpret correctly UTF-8 special
	 *現在符合規範</a>和正確解釋UTF-8特殊
	 * characters without requiring a {@code charset=UTF-8} parameter.
	 *不需要{@code字符集=UTF-8}參數的字符。
	 */
	@Deprecated
	public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";
	
	public static final String APPLICATION_JSON_VALUE = "application/json";

刪除

	public static void deleteUser(Long id) {
	    RestTemplate restTmpl = new RestTemplate();
	    restTmpl.delete("http://localhost:8080/user/{id}", id);
	}
  • restTemplate 是用 HttpURLConnection 的,不支持 PATCH
  • PATCH 是對 PUT的補充,

獲取響應頭,狀態碼,和 資源交換

	public static User insertUserEntity(UserVo newUserVo) {
	    // 請求頭
	    HttpHeaders headers = new HttpHeaders();
	    // 請求類型
	    headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
	    // 綁定請求體和頭
	    HttpEntity<UserVo> request = new HttpEntity<>(newUserVo, headers);
	    RestTemplate restTmpl = new RestTemplate();
        
	    // 請求服務器
	    ResponseEntity<User> userEntity = restTmpl.postForEntity(
	        "http://localhost:8080/user2/entity", request, User.class);
        
	    // 獲取響應體
	    User user = userEntity.getBody();
        
	    // 獲取響應頭
	    HttpHeaders respHeaders = userEntity.getHeaders();
        
	    // 獲取響應屬性
	    List<String> success = respHeaders.get("success");
        
	    // 響應的HTTP狀態碼
	    int status = userEntity.getStatusCodeValue();
        
	    System.out.println(user.getId());
	    return user;
	}
  • ResponseEntity userEntity = restTmpl.postForEntity()

  • 包含響應體 和 狀態碼 和 響應頭

  • exchange 資源交換使用:

    	public static User useExchange(UserVo newUserVo, Long id) {
    	    // 請求頭
    	    HttpHeaders headers = new HttpHeaders();
    	    // 請求類型
    	    headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
    	    // 綁定請求體和頭
    	    HttpEntity<UserVo> request = new HttpEntity<>(newUserVo, headers);
    	    RestTemplate restTmpl = new RestTemplate();
            
    	    String url = "http://localhost:8080/user2/entity";
            
    	    // 請求服務器
    	    ResponseEntity<User> userEntity 
    	        = restTmpl.exchange(url, HttpMethod.POST, request, User.class);
            
    	    // 獲取響應體
    	    User user = userEntity.getBody();
            
    	    // 獲取響應頭
    	    HttpHeaders respHeaders = userEntity.getHeaders();
    	    // 響應頭屬性
    	    List<String> success = respHeaders.get("success");
            
    	    // 響應的HTTP狀態碼
    	    int status = userEntity.getStatusCodeValue();
            
    	    System.out.println(user.getId());
            
    	    // 修改URL獲取資源
    	    url = "http://localhost:8080/user/{id}";
            
    	    // 傳遞URL地址參數
    	    ResponseEntity<UserVo> userVoEntity 
    	        = restTmpl.exchange(url, HttpMethod.GET, null, UserVo.class, id);
            
    	    // 獲取響應體
    	    UserVo userVo = userVoEntity.getBody();
    	    System.out.println(userVo.getUserName());
    	    return user;
    	}
    
    • 更多的時候,還是用 PostForEntity 和 getForEntity方法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章