Spring RestTemplate 总结
URI 构造
要轻松地操作 URL,可以使用 Spring 的 org.springframework.web.util.UriComponentsBuilder
类。相比手动拼接字符串更易维护,还可以处理 URL 编码:
1 | // `java.net.URI#toURL()` 可以转为 `java.net.URL` |
GenericUtils
参考:https://github.com/qidawu/java-api-test/blob/master/src/main/java/reflect/GenericUtils.java
HTTP 请求
RestTemplate
构造出 java.net.URI
之后,可以使用 org.springframework.web.client.RestTemplate
如下:
1 | import lombok.extern.slf4j.Slf4j; |
GET 请求、POST 表单时,对方 Controller 方法入参需标注 @RequestParam
,以接收 query parameter。但要注意,官方文档提醒如下:
Supported for annotated handler methods in Spring MVC and Spring WebFlux as follows:
- In Spring MVC, “request parameters” map to query parameters, form data, and parts in multipart requests. This is because the Servlet API combines query parameters and form data into a single map called “parameters”, and that includes automatic parsing of the request body.
- In Spring WebFlux, “request parameters” map to query parameters only. To work with all 3, query, form data, and multipart data, you can use data binding to a command object annotated with
ModelAttribute
.
CURL 形式
GET 请求
1 | curl --location --request GET 'http://rootUrl/path?key=value' \ |
POST 表单
1 | curl --location --request POST 'http://rootUrl/path' \ |
POST JSON
1 | curl --location --request POST 'http://rootUrl/path' \ |
核心类解析
涉及的核心类如下:
org.springframework.*
org.springframework.web.client.RestTemplate
Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK
HttpURLConnection
, Apache HttpComponents, and others.The RestTemplate offers templates for common scenarios by HTTP method, in addition to the generalized
exchange
andexecute
methods that support of less frequent cases.
org.springframework.http.converter.HttpMessageConverter
Strategy interface for converting from and to HTTP requests and responses.
参考:
java.net.*
Class
URL
represents a Uniform Resource Locator, a pointer to a “resource” on the World Wide Web. A resource can be something as simple as a file or a directory, or it can be a reference to a more complicated object, such as a query to a database or to a search engine. More information on the types of URLs and their formats can be found at: Types of URL
The abstract class
URLConnection
is the superclass of all classes that represent a communications link between the application and aURL
. Instances of this class can be used both to read from and to write to the resource referenced by theURL
.
URLConnection
的继承结构如下:
A
URLConnection
with support for HTTP-specific features. See the spec for details.
This class implements client sockets (also called just “sockets”). A socket is an endpoint for communication between two machines.
The actual work of the socket is performed by an instance of the
SocketImpl
class. An application, by changing the socket factory that creates the socket implementation, can configure itself to create sockets appropriate to the local firewall.
The abstract class
SocketImpl
is a common superclass of all classes that actually implement sockets. It is used to create both client and server sockets.
当通过 Socket
类的默认无参构造方法 new Socket()
创建 socket 对象时,其底层实现如下图。从下图可见,将会创建抽象类 SocketImpl
的默认实现类 SocksSocketImpl
:
而 SocksSocketImpl
的继承结构如下。
Socket
类提供了 getOutputStream()
、getInputStream()
方法,其底层实现获取 AbstractPlainSocketImpl
的两个私有成员变量,如下:
java.net.SocketInputSteram
,核心方法:java.net.SocketOutputStream
,核心方法:这两个类的继承结构如下:
java.io.*
Instances of the file descriptor class serve as an opaque handle to the underlying machine-specific structure representing an open file, an open socket, or another source or sink of bytes. The main practical use for a file descriptor is to create a
FileInputStream
orFileOutputStream
to contain it.Applications should not create their own file descriptors.
常见异常
下面介绍网络编程时,常见的 Socket、HTTP 异常。详见:https://docs.oracle.com/javase/8/docs/api/java/net/package-summary.html
IOException
In case the TCP handshakes are not complete, the connection remains unsuccessful. Consequently, the program throws an
IOException
indicating an error occurred while establishing a new connection.
BindException
Signals that an error occurred while attempting to bind a socket to a local address and port.
问题:
1 | java.net.BindException: Address already in use (Bind failed) |
原因:server-side 未成功 bind()
到指定端口号(如端口被其它服务占用)。
ConnectException
Signals that an error occurred while attempting to connect a socket to a remote address and port.
Connection refused
问题:
1 | java.net.ConnectException: Connection refused (Connection refused) |
原因:client-side connect()
建立 TCP 连接失败
- client-side
connect()
错了服务端口号; - server-side 服务未启动、未在
listen()
监听、或listen()
的backlog
队列数无法满足 client-side 并发连接请求数; - 无法完成 TCP 三次握手:
Connection timed out
问题:
1 | java.net.ConnectException: Connection timed out (Connection timed out) |
原因:client-side connect()
超时:
1 | Socket socket = new Socket(); |
连接超时原因:client-side 发出 sync
包之后,server-side 未在指定时间内回复 ack
导致的。没有回复 ack
的原因可能是网络丢包、防火墙阻止服务端返回 syn
的 ack
包等。
防火墙原因
Sometimes, firewalls block certain ports due to security reasons. As a result, a “connection timed out” error can occur when a client is trying to establish a connection to a server. Therefore, we should check the firewall settings to see if it’s blocking a port before binding it to a service.
SocketTimeoutException
java.net.SocketTimeoutException
Signals that a timeout has occurred on a socket
accept()
orread()
.
Accept timed out
问题:
1 | java.net.SocketTimeoutException: Accept timed out |
原因:server-side accept()
超时:
1 | ServerSocket serverSocket = new ServerSocket(port, backlog); |
Read timed out
问题:
1 | java.net.SocketTimeoutException: Read timed out |
原因:server-side / client-side read()
超时:
1 | // server-side |
SocketException
Thrown to indicate that there is an error creating or accessing a Socket.
Connection reset by peer
1 | java.net.SocketException: Connection reset by peer (connect failed) |
Bad file descriptor
1 | java.net.SocketException: Bad file descriptor (Write failed) |
Broken pipe
1 | java.net.SocketException: Broken pipe (Write failed) |
HttpClientErrorException
org.springframework.web.client.HttpClientErrorException
Exception thrown when an HTTP
4xx
is received.
HttpServerErrorException
org.springframework.web.client.HttpServerErrorException
Exception thrown when an HTTP
5xx
is received.
…
参考
https://docs.oracle.com/javase/8/docs/technotes/guides/net/index.html