带有可选参数请求的微服务通信
Microservices communication with optional parameters requests
我有一个客房服务,当请求 http://localhost:8082/room/search/byRoomChar
时,returns 房间的详细信息
@GetMapping(path = "/search/byRoomChar")
public @ResponseBody List<Room> byCapacity(@RequestParam(required = false) Integer capacity,
@RequestParam(required = false) Boolean isUnderMaintenance,
@RequestParam(required = false) String equipment) {
return roomRepository.findByRoomChar(capacity, isUnderMaintenance, equipment);
}
现在我想从预订服务请求这个@GetMapping,因为这是用户将使用 http://localhost:8081/booking/search/byRoomChar.[=15= 与之交互的应用程序网关]
@GetMapping(path = "/search/byRoomChar")
public @ResponseBody List<Room> byCapacity(@RequestParam(required = false) Integer capacity,
@RequestParam(required = false) Boolean isUnderMaintenance,
@RequestParam(required = false) String equipment) {
ResponseEntity<Room[]> roomsResponse = restTemplate.getForEntity("http://localhost:8082/room/search/byRoomChar?capacity=" + capacity + "&isUnderMaintenance=" +
isUnderMaintenance + "&equipment=" + equipment, Room[].class);
return Arrays.asList(roomsResponse.getBody());
}
房间实体代码:
package nl.tudelft.sem.template.entities;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "Room")
public class Room {
@EmbeddedId
private RoomId id;
@Column(name = "capacity")
private int capacity;
@Column(name = "numberOfPeople")
private int numberOfPeople;
@Column(name = "isUnderMaintenance", nullable = false)
private boolean isUnderMaintenance;
@Column(name = "equipment")
private String equipment;
public Room() {
}
public Room(long roomNumber, long buildingNumber, int capacity,
int numberOfPeople, boolean isUnderMaintenance, String equipment) {
RoomId id = new RoomId(roomNumber, buildingNumber);
this.id = id;
this.capacity = capacity;
this.numberOfPeople = numberOfPeople;
this.isUnderMaintenance = isUnderMaintenance;
this.equipment = equipment;
}
public RoomId getId() {
return id;
}
public void setId(RoomId id) {
this.id = id;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public int getNumberOfPeople() {
return numberOfPeople;
}
public void setNumberOfPeople(int numberOfPeople) {
this.numberOfPeople = numberOfPeople;
}
public boolean getIsUnderMaintenance() {
return isUnderMaintenance;
}
public void setUnderMaintenance(boolean underMaintenance) {
isUnderMaintenance = underMaintenance;
}
public String getEquipment() {
return equipment;
}
public void setEquipment(String equipment) {
this.equipment = equipment;
}
}
房间存储库代码:
package nl.tudelft.sem.template.repositories;
import java.util.List;
import nl.tudelft.sem.template.entities.Room;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface RoomRepository extends JpaRepository<Room, Integer> {
@Query("SELECT r FROM Room r WHERE (:number is null or r.id.number = :number)"
+ "and r.id.buildingNumber = :buildingNumber")
List<Room> findByRoomNum(@Param("number") Long number,
@Param("buildingNumber") Long buildingNumber);
@Query("SELECT r FROM Room r WHERE (:capacity is null or r.capacity = :capacity) and"
+ "(:isUnderMaintenance is null or r.isUnderMaintenance = :isUnderMaintenance) and"
+ "(:equipment is null or r.equipment = :equipment)")
List<Room> findByRoomChar(@Param("capacity") Integer capacity,
@Param("isUnderMaintenance") Boolean isUnderMaintenance,
@Param("equipment") String equipment);
}
但是,这不起作用,因为在从预订服务调用 getmapping 时省略参数时,由于 required=false,所有参数值都变为 null。并将其转换为硬编码 url.
中的字符串
2021-12-04 17:13:03.883 WARN 16920 --- [nio-8082-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Boolean'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [null]]
如何从代码中发出带有可选参数的 get http 请求?
这是因为参数为空时构建的URI字符串如下:
http://localhost:8082/room/search/byRoomChar?isUnderMaintenance=null
由于“null”被附加为参数值,房间服务器无法尝试将其反序列化为不同的类型。
例如,在您提供的错误消息中,“isUnderMaintenance”应该是布尔值,但却是“空”字符串。
为了解决这个问题,我推荐使用UriComponentBuilder
。
@Test
fun constructUriWithQueryParameter() {
val uri = UriComponentsBuilder.newInstance()
.scheme("http")
.host("localhost")
.port(8082)
.path("/room/search/byRoomChar")
.query("capacity={capa}")
.query("isUnderMaintenance={isUnderMaintenance}")
.query("equipment={equip}")
.buildAndExpand(null, null, null)
.toUriString()
assertEquals(
"http://localhost:8082/room/search/byRoomChar
capacity=&isUnderMaintenance=&equipment=",
uri
)
}
UriComponentsBuilder
可以帮助构建 URI。它正确处理可为空的查询参数。
String uri = UriComponentsBuilder.fromHttpUrl("http://localhost:8082/room/search/byRoomChar")
.queryParam("capacity", capacity)
.queryParam("isUnderMaintenance", isUnderMaintenance)
.queryParam("equipment", equipment)
.encode().toUriString();
ResponseEntity<Room[]> roomsResponse = restTemplate.getForEntity(uri, Room[].class);
此外,以下答案可能会有所帮助:
如果这些参数在您的 Room API 中不是强制性的,但您仍然在调用数据库时使用它们,或者您有合理的默认值,如果它们实际上不是由用户提供的。以下几行内容(在这种情况下,您实际上不需要明确定义 required = false
):
@GetMapping(path = "/search/byRoomChar")
public @ResponseBody List<Room> byCapacity(@RequestParam(defaultValue = "10") Integer capacity,
@RequestParam(defaultValue = "false") Boolean isUnderMaintenance,
@RequestParam(defaultValue = "default-equipment") String equipment) {
return roomRepository.findByRoomChar(capacity, isUnderMaintenance, equipment);
}
或者您定义一个没有附加参数的 Repository 方法,但这可能更棘手,因为您基本上需要所有可能的 null 和非 null 参数。
我尝试了 Petr Aleksandrov 的回答。看起来很干净,这很可能是要走的路,但我遇到了“不是绝对 uri”的异常。
没有时间寻找答案,所以我创建了解决方法代码。凌乱但有效。
@GetMapping(path = "/search/byRoomChar")
public @ResponseBody List<Room> byCapacity(@RequestParam(required = false) Integer capacity,
@RequestParam(required = false) Boolean isUnderMaintenance,
@RequestParam(required = false) String equipment) {
if(capacity == null && isUnderMaintenance == null && equipment == null) {
ResponseEntity<Room[]> roomsResponse = restTemplate.getForEntity("http://localhost:8082/room/search/byRoomChar", Room[].class);
return Arrays.asList(roomsResponse.getBody());
}
String url = "http://localhost:8082/room/search/byRoomChar?";
if(capacity != null) {
url += "capacity=" + capacity + "&";
}
if(isUnderMaintenance != null) {
url += "isUnderMaintenance=" + isUnderMaintenance + "";
}
if(equipment != null) {
url += "equipment=" + equipment;
}
ResponseEntity<Room[]> roomsResponse = restTemplate.getForEntity(url, Room[].class);
return Arrays.asList(roomsResponse.getBody());
}
我有一个客房服务,当请求 http://localhost:8082/room/search/byRoomChar
时,returns 房间的详细信息 @GetMapping(path = "/search/byRoomChar")
public @ResponseBody List<Room> byCapacity(@RequestParam(required = false) Integer capacity,
@RequestParam(required = false) Boolean isUnderMaintenance,
@RequestParam(required = false) String equipment) {
return roomRepository.findByRoomChar(capacity, isUnderMaintenance, equipment);
}
现在我想从预订服务请求这个@GetMapping,因为这是用户将使用 http://localhost:8081/booking/search/byRoomChar.[=15= 与之交互的应用程序网关]
@GetMapping(path = "/search/byRoomChar")
public @ResponseBody List<Room> byCapacity(@RequestParam(required = false) Integer capacity,
@RequestParam(required = false) Boolean isUnderMaintenance,
@RequestParam(required = false) String equipment) {
ResponseEntity<Room[]> roomsResponse = restTemplate.getForEntity("http://localhost:8082/room/search/byRoomChar?capacity=" + capacity + "&isUnderMaintenance=" +
isUnderMaintenance + "&equipment=" + equipment, Room[].class);
return Arrays.asList(roomsResponse.getBody());
}
房间实体代码:
package nl.tudelft.sem.template.entities;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "Room")
public class Room {
@EmbeddedId
private RoomId id;
@Column(name = "capacity")
private int capacity;
@Column(name = "numberOfPeople")
private int numberOfPeople;
@Column(name = "isUnderMaintenance", nullable = false)
private boolean isUnderMaintenance;
@Column(name = "equipment")
private String equipment;
public Room() {
}
public Room(long roomNumber, long buildingNumber, int capacity,
int numberOfPeople, boolean isUnderMaintenance, String equipment) {
RoomId id = new RoomId(roomNumber, buildingNumber);
this.id = id;
this.capacity = capacity;
this.numberOfPeople = numberOfPeople;
this.isUnderMaintenance = isUnderMaintenance;
this.equipment = equipment;
}
public RoomId getId() {
return id;
}
public void setId(RoomId id) {
this.id = id;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public int getNumberOfPeople() {
return numberOfPeople;
}
public void setNumberOfPeople(int numberOfPeople) {
this.numberOfPeople = numberOfPeople;
}
public boolean getIsUnderMaintenance() {
return isUnderMaintenance;
}
public void setUnderMaintenance(boolean underMaintenance) {
isUnderMaintenance = underMaintenance;
}
public String getEquipment() {
return equipment;
}
public void setEquipment(String equipment) {
this.equipment = equipment;
}
}
房间存储库代码:
package nl.tudelft.sem.template.repositories;
import java.util.List;
import nl.tudelft.sem.template.entities.Room;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface RoomRepository extends JpaRepository<Room, Integer> {
@Query("SELECT r FROM Room r WHERE (:number is null or r.id.number = :number)"
+ "and r.id.buildingNumber = :buildingNumber")
List<Room> findByRoomNum(@Param("number") Long number,
@Param("buildingNumber") Long buildingNumber);
@Query("SELECT r FROM Room r WHERE (:capacity is null or r.capacity = :capacity) and"
+ "(:isUnderMaintenance is null or r.isUnderMaintenance = :isUnderMaintenance) and"
+ "(:equipment is null or r.equipment = :equipment)")
List<Room> findByRoomChar(@Param("capacity") Integer capacity,
@Param("isUnderMaintenance") Boolean isUnderMaintenance,
@Param("equipment") String equipment);
}
但是,这不起作用,因为在从预订服务调用 getmapping 时省略参数时,由于 required=false,所有参数值都变为 null。并将其转换为硬编码 url.
中的字符串2021-12-04 17:13:03.883 WARN 16920 --- [nio-8082-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Boolean'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [null]]
如何从代码中发出带有可选参数的 get http 请求?
这是因为参数为空时构建的URI字符串如下:
http://localhost:8082/room/search/byRoomChar?isUnderMaintenance=null
由于“null”被附加为参数值,房间服务器无法尝试将其反序列化为不同的类型。 例如,在您提供的错误消息中,“isUnderMaintenance”应该是布尔值,但却是“空”字符串。
为了解决这个问题,我推荐使用UriComponentBuilder
。
@Test
fun constructUriWithQueryParameter() {
val uri = UriComponentsBuilder.newInstance()
.scheme("http")
.host("localhost")
.port(8082)
.path("/room/search/byRoomChar")
.query("capacity={capa}")
.query("isUnderMaintenance={isUnderMaintenance}")
.query("equipment={equip}")
.buildAndExpand(null, null, null)
.toUriString()
assertEquals(
"http://localhost:8082/room/search/byRoomChar
capacity=&isUnderMaintenance=&equipment=",
uri
)
}
UriComponentsBuilder
可以帮助构建 URI。它正确处理可为空的查询参数。
String uri = UriComponentsBuilder.fromHttpUrl("http://localhost:8082/room/search/byRoomChar")
.queryParam("capacity", capacity)
.queryParam("isUnderMaintenance", isUnderMaintenance)
.queryParam("equipment", equipment)
.encode().toUriString();
ResponseEntity<Room[]> roomsResponse = restTemplate.getForEntity(uri, Room[].class);
此外,以下答案可能会有所帮助:
如果这些参数在您的 Room API 中不是强制性的,但您仍然在调用数据库时使用它们,或者您有合理的默认值,如果它们实际上不是由用户提供的。以下几行内容(在这种情况下,您实际上不需要明确定义 required = false
):
@GetMapping(path = "/search/byRoomChar")
public @ResponseBody List<Room> byCapacity(@RequestParam(defaultValue = "10") Integer capacity,
@RequestParam(defaultValue = "false") Boolean isUnderMaintenance,
@RequestParam(defaultValue = "default-equipment") String equipment) {
return roomRepository.findByRoomChar(capacity, isUnderMaintenance, equipment);
}
或者您定义一个没有附加参数的 Repository 方法,但这可能更棘手,因为您基本上需要所有可能的 null 和非 null 参数。
我尝试了 Petr Aleksandrov 的回答。看起来很干净,这很可能是要走的路,但我遇到了“不是绝对 uri”的异常。
没有时间寻找答案,所以我创建了解决方法代码。凌乱但有效。
@GetMapping(path = "/search/byRoomChar")
public @ResponseBody List<Room> byCapacity(@RequestParam(required = false) Integer capacity,
@RequestParam(required = false) Boolean isUnderMaintenance,
@RequestParam(required = false) String equipment) {
if(capacity == null && isUnderMaintenance == null && equipment == null) {
ResponseEntity<Room[]> roomsResponse = restTemplate.getForEntity("http://localhost:8082/room/search/byRoomChar", Room[].class);
return Arrays.asList(roomsResponse.getBody());
}
String url = "http://localhost:8082/room/search/byRoomChar?";
if(capacity != null) {
url += "capacity=" + capacity + "&";
}
if(isUnderMaintenance != null) {
url += "isUnderMaintenance=" + isUnderMaintenance + "";
}
if(equipment != null) {
url += "equipment=" + equipment;
}
ResponseEntity<Room[]> roomsResponse = restTemplate.getForEntity(url, Room[].class);
return Arrays.asList(roomsResponse.getBody());
}