
(
插图 )
99.9%的开发人员面临的挑战之一是访问第三方端点。 它既可以是外部API,也可以是“您自己的”微服务。 现在,每个人都在使用微服务,是的。 检索或发送数据很简单,但是有时它们会重新发明轮子。 您能说出5种使用Java实现查询的方法(使用库和不使用库)吗? 否-欢迎猫。 ?? 来比较;)
0.简介
我们将解决的任务非常简单:我们需要发送GET / POST请求并获得JSON格式的响应。 为了不编写其他原始微服务,我将使用
一个示例 ,
该示例为一组端点提供一些数据。 所有代码示例均已得到最大程度的简化,此处不会出现带有身份验证令牌和标头的棘手情况。 仅POST和GET,GET和POST等5次左右。
所以走吧
1.内置Java解决方案
如果不使用第三方库就无法解决任务,那将很奇怪。 当然可以! 但是可悲。 java.net程序包,即HttpURLConnection,URL和URLEnconder。
要发送GET,POST请求,您需要创建一个URL对象并基于该对象打开一个连接:
final URL url = new URL("http://jsonplaceholder.typicode.com/posts?_limit=10"); final HttpURLConnection con = (HttpURLConnection) url.openConnection();
接下来,您需要使用所有参数来简化连接:
con.setRequestMethod("GET"); con.setRequestProperty("Content-Type", "application/json"); con.setConnectTimeout(CONNECTION_TIMEOUT); con.setReadTimeout(CONNECTION_TIMEOUT);
并获得一个InputStream,从此处读取接收到的所有数据。
try (final BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()))) { String inputLine; final StringBuilder content = new StringBuilder(); while ((inputLine = in.readLine()) != null) { content.append(inputLine); } return content.toString(); } catch (final Exception ex) { ex.printStackTrace(); return ""; }
而且,实际上,我们在这里得到了这样的答案(所有后续示例都将是相同的,因为我们使用相同的端点):
[ { "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" }, { "userId": 1, "id": 2, "title": "qui est esse", "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla" }, ... { "userId": 1, "id": 9, "title": "nesciunt iure omnis dolorem tempora et accusantium", "body": "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas" }, { "userId": 1, "id": 10, "title": "optio molestias id quia eum", "body": "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error" } ]
在POST请求的情况下,一切都比较复杂。 我们不仅希望接收答案,而且希望传输数据。 为此,我们需要将它们放在那里。 该文档告诉我们,这可能如下工作:
final Map<String, String> parameters = new HashMap<>(); parameters.put("title", "foo"); parameters.put("body", "bar"); parameters.put("userId", "1"); con.setDoOutput(true); final DataOutputStream out = new DataOutputStream(con.getOutputStream()); out.writeBytes(getParamsString(parameters)); out.flush(); out.close();
其中getParamsString是将Map映射到包含键值对的String的简单方法:
public static String getParamsString(final Map<String, String> params) { final StringBuilder result = new StringBuilder(); params.forEach((name, value) -> { try { result.append(URLEncoder.encode(name, "UTF-8")); result.append('='); result.append(URLEncoder.encode(value, "UTF-8")); result.append('&'); } catch (final UnsupportedEncodingException e) { e.printStackTrace(); } }); final String resultString = result.toString(); return !resultString.isEmpty() ? resultString.substring(0, resultString.length() - 1) : resultString; }
成功创建后,我们将对象取回:
{ "title": "foo", "body": "bar", "userId": "1", "id": 101}
对可以运行的源
的引用 。
2. Apache HttpClient
如果我们摆脱了内置解决方案,那么我们遇到的第一件事就是Apache的HttpClient。 要进行访问,我们需要JAR文件本身。 或者,由于我使用的是Maven,因此相应的依赖项为:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version> </dependency>
使用HttpClient查询的方式已经好多了(
源 ):
final CloseableHttpClient httpclient = HttpClients.createDefault(); final HttpUriRequest httpGet = new HttpGet("http://jsonplaceholder.typicode.com/posts?_limit=10"); try ( CloseableHttpResponse response1 = httpclient.execute(httpGet) ){ final HttpEntity entity1 = response1.getEntity(); System.out.println(EntityUtils.toString(entity1)); } final HttpPost httpPost = new HttpPost("http://jsonplaceholder.typicode.com/posts"); final List<NameValuePair> params = new ArrayList<>(); params.add(new BasicNameValuePair("title", "foo")); params.add(new BasicNameValuePair("body", "bar")); params.add(new BasicNameValuePair("userId", "1")); httpPost.setEntity(new UrlEncodedFormEntity(params)); try ( CloseableHttpResponse response2 = httpclient.execute(httpPost) ){ final HttpEntity entity2 = response2.getEntity(); System.out.println(EntityUtils.toString(entity2)); } httpclient.close();
我们得到了相同的数据,但是编写了一半的代码。 我想知道在这种看似基本的问题上,他们还能在什么地方领导搜索? 但是Apache有另一个模块可以解决我们的问题。
3. Apache Fluent API
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>fluent-hc</artifactId> <version>4.5.6</version> </dependency>
并且已经使用Fluent Api,我们的调用变得更加可读(
source ):
final Content getResult = Request.Get("http://jsonplaceholder.typicode.com/posts?_limit=10") .execute().returnContent(); System.out.println(getResult.asString()); final Collection<NameValuePair> params = new ArrayList<>(); params.add(new BasicNameValuePair("title", "foo")); params.add(new BasicNameValuePair("body", "bar")); params.add(new BasicNameValuePair("userId", "1")); final Content postResultForm = Request.Post("http://jsonplaceholder.typicode.com/posts") .bodyForm(params, Charset.defaultCharset()) .execute().returnContent(); System.out.println(postResultForm.asString());
作为奖励-一个例子。 如果我们不想将数据作为表单而是作为每个人最喜欢的JSON传输到正文,请执行以下操作:
final Content postResult = Request.Post("http://jsonplaceholder.typicode.com/posts") .bodyString("{\"title\": \"foo\",\"body\":\"bar\",\"userId\": 1}", ContentType.APPLICATION_JSON) .execute().returnContent(); System.out.println(postResult.asString());
实际上,这些调用已分解为一行代码。 对我而言,这比第一种方法对开发人员更加友好。
4. Spring RestTemplate
接下来呢? 进一步的经验将我带入了Spring的世界。 而且,毫不奇怪,spring还具有解决我们简单任务的工具(奇怪,对吗?一项任务,甚至不是一个-需要!-从根本上讲,但由于某种原因,有不止一种解决方案)。 在Spring生态系统中找到的第一个解决方案(基本)是RestTemplate。 为此,我们需要拉动整个动物园的相当一部分。 因此,如果您需要在NONSpring应用程序中发送请求,那么最好不要拖曳整个厨房。 如果已经有了春天,那为什么不呢? 如何获取为此所需的一切,请参见
此处 。 好吧,实际上使用RestTemplate的GET请求看起来像这样:
final RestTemplate restTemplate = new RestTemplate(); final String stringPosts = restTemplate.getForObject("http://jsonplaceholder.typicode.com/posts?_limit=10", String.class); System.out.println(stringPosts);
引擎盖。 但是! 我不想再使用字符串了,因此有机会不接收字符串,而是希望接收的现成对象! 创建一个Post对象:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Builder; import lombok.Getter; import lombok.Setter; @Builder @Getter @Setter @JsonIgnoreProperties(ignoreUnknown = true) public class Post { private int id; private String title; private String body; private int userId; public String toString() { return String.format("\n id: %s \n title: %s \n body: %s \n userId: %s \n", id, title, body, userId); } }
在这里:
建造者,吸气剂,塞特犬 -来自龙目岛的糖,不要手工书写。 是的,她在这里,懒惰的母亲。
JsonIgnoreProperties-以便在接收到未知字段的情况下,不会崩溃而导致错误,而要使用我们知道的字段。
好吧,
toString ,将我们的对象输出到控制台,这可以读取。 好吧,实际上我们的GET和POST请求被转换为(
source ):
而且我们已经有了对象,而不需要解析自己的字符串。
库尔 现在我们可以围绕RestTemplate编写一些包装器,以便正确构建查询。 看起来还不错,但是就我而言,仍然可以改进。 编写的代码越少,出错的机会就越少。 大家都知道主要的问题经常是PEBCAK(椅子和键盘之间存在问题)...
5.春季假装
接下来是Feign阶段,它是Spring Cloud的一部分。 首先,将Feign依赖项添加到之前已经添加的spring环境中:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> <version>1.4.5.RELEASE</version> </dependency>
实际上,所需要做的就是声明该接口并为其添加一个很好的小注释。 特别是对于使用spring编写控制器的人来说,这种方法很有吸引力。
这是我们通过Feign(
source )发送请求所需要做的。
@FeignClient(name = "jsonplaceholder", url = "http://jsonplaceholder.typicode.com", path = "/posts") public interface ApiClient { @RequestMapping(method = GET, value = "/", consumes = APPLICATION_JSON_VALUE) List<Post> getPosts(@RequestParam("_limit") final int postLimit); @RequestMapping(method = POST, value = "/", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE) Post savePost(@RequestBody Post post); }
美丽,不是吗? 是的,在这里可以很好地重用我们为RestTemplate编写的数据模型。
6.结论
除了介绍的五种方法外,没有其他实现方法。 此收藏仅反映了作者在我遇到他们并开始在项目中使用它们时的经历。 现在,我积极使用Feign,享受生活并等待其他更方便的出现,以便您可以对监视器大喊“ <library name>,我选择了您!” -一切准备就绪,可以使用和集成。 同时,Feign。
PS作为您可以考虑生成的客户端Swagger的“惰性”方法之一。 但是,正如他们所说,有细微差别。 并非所有的开发人员都使用Swagger来记录其API,甚至没有那么多的开发人员以如此高的质量来编写它们,以至于他们可以轻松地生成和使用客户端,而没有获得昆虫学的集合,这样做弊大于利。