深入浅出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方法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章