|
|
@@ -0,0 +1,299 @@
|
|
|
+package com.storlead.knowledge.utils;
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.storlead.framework.common.result.Result;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.net.URI;
|
|
|
+import java.net.http.*;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.Path;
|
|
|
+import java.time.Duration;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.UUID;
|
|
|
+import java.util.function.Function;
|
|
|
+
|
|
|
+@Component
|
|
|
+public class HttpService {
|
|
|
+
|
|
|
+ private final HttpClient httpClient;
|
|
|
+ private final ObjectMapper objectMapper = JacksonHolder.OBJECT_MAPPER;
|
|
|
+
|
|
|
+ public HttpService() {
|
|
|
+ this.httpClient = HttpClient.newBuilder()
|
|
|
+ .connectTimeout(Duration.ofSeconds(5))
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ================= Base Request ================= */
|
|
|
+
|
|
|
+ private HttpRequest.Builder baseRequest(String authorization) {
|
|
|
+ HttpRequest.Builder builder = HttpRequest.newBuilder()
|
|
|
+ .timeout(Duration.ofSeconds(10))
|
|
|
+ .header("Content-Type", "application/json");
|
|
|
+
|
|
|
+ if (authorization != null && !authorization.isBlank()) {
|
|
|
+ builder.header("Authorization", authorization);
|
|
|
+ }
|
|
|
+ return builder;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ================= Response Handler ================= */
|
|
|
+
|
|
|
+ private <T> Result<T> handleResponse(
|
|
|
+ HttpResponse<String> response,
|
|
|
+ Function<String, T> deserializer
|
|
|
+ ) {
|
|
|
+ int status = response.statusCode();
|
|
|
+ String body = response.body();
|
|
|
+
|
|
|
+ if (status >= 200 && status < 300) {
|
|
|
+ try {
|
|
|
+ return Result.ok(deserializer.apply(body));
|
|
|
+ } catch (Exception e) {
|
|
|
+ return (Result<T>) Result.error( "json 解析失败:"+e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return (Result<T>) Result.error("HTTP " + status + ": " + body);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ================= GET ================= */
|
|
|
+
|
|
|
+ public <T> Result<T> get(
|
|
|
+ String url,
|
|
|
+ Map<String, ?> queryParams,
|
|
|
+ String authorization,
|
|
|
+ TypeReference<T> typeReference
|
|
|
+ ) {
|
|
|
+ try {
|
|
|
+ String fullUrl = UrlBuilder.build(url, queryParams);
|
|
|
+ HttpRequest request = baseRequest(authorization)
|
|
|
+ .uri(URI.create(fullUrl))
|
|
|
+ .GET()
|
|
|
+ .build();
|
|
|
+
|
|
|
+ HttpResponse<String> response =
|
|
|
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+
|
|
|
+ return handleResponse(response,
|
|
|
+ body -> {
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(body, typeReference);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ return (Result<T>) Result.error(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ================= POST ================= */
|
|
|
+
|
|
|
+ public <T> Result<T> post(
|
|
|
+ String url,
|
|
|
+ Map<String, ?> queryParams,
|
|
|
+ String authorization,
|
|
|
+ String body,
|
|
|
+ TypeReference<T> typeReference
|
|
|
+ ) {
|
|
|
+ try {
|
|
|
+ String fullUrl = UrlBuilder.build(url, queryParams);
|
|
|
+ HttpRequest request = baseRequest(authorization)
|
|
|
+ .uri(URI.create(fullUrl))
|
|
|
+ .POST(HttpRequest.BodyPublishers.ofString(body))
|
|
|
+ .build();
|
|
|
+
|
|
|
+ HttpResponse<String> response =
|
|
|
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+
|
|
|
+ return handleResponse(response,
|
|
|
+ res -> {
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(res, typeReference);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ return (Result<T>) Result.error(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ================= PUT ================= */
|
|
|
+
|
|
|
+ public <T> Result<T> put(
|
|
|
+ String url,
|
|
|
+ Map<String, ?> queryParams,
|
|
|
+ String authorization,
|
|
|
+ String body,
|
|
|
+ TypeReference<T> typeReference
|
|
|
+ ) {
|
|
|
+ try {
|
|
|
+ String fullUrl = UrlBuilder.build(url, queryParams);
|
|
|
+ HttpRequest request = baseRequest(authorization)
|
|
|
+ .uri(URI.create(fullUrl))
|
|
|
+ .PUT(HttpRequest.BodyPublishers.ofString(body))
|
|
|
+ .build();
|
|
|
+
|
|
|
+ HttpResponse<String> response =
|
|
|
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+
|
|
|
+ return handleResponse(response,
|
|
|
+ res -> {
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(res, typeReference);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ return (Result<T>) Result.error(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ================= PATCH ================= */
|
|
|
+
|
|
|
+ public <T> Result<T> patch(
|
|
|
+ String url,
|
|
|
+ Map<String, ?> queryParams,
|
|
|
+ String authorization,
|
|
|
+ String body,
|
|
|
+ TypeReference<T> typeReference
|
|
|
+ ) {
|
|
|
+ try {
|
|
|
+ String fullUrl = UrlBuilder.build(url, queryParams);
|
|
|
+ HttpRequest request = baseRequest(authorization)
|
|
|
+ .uri(URI.create(fullUrl))
|
|
|
+ .method("PATCH", HttpRequest.BodyPublishers.ofString(body))
|
|
|
+ .build();
|
|
|
+
|
|
|
+ HttpResponse<String> response =
|
|
|
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+
|
|
|
+ return handleResponse(response,
|
|
|
+ res -> {
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(res, typeReference);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ return (Result<T>) Result.error(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ================= DELETE ================= */
|
|
|
+
|
|
|
+ public <T> Result<T> delete(
|
|
|
+ String url,
|
|
|
+ Map<String, ?> queryParams,
|
|
|
+ String authorization,
|
|
|
+ TypeReference<T> typeReference
|
|
|
+ ) {
|
|
|
+ try {
|
|
|
+ String fullUrl = UrlBuilder.build(url, queryParams);
|
|
|
+ HttpRequest request = baseRequest(authorization)
|
|
|
+ .uri(URI.create(fullUrl))
|
|
|
+ .DELETE()
|
|
|
+ .build();
|
|
|
+
|
|
|
+ HttpResponse<String> response =
|
|
|
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+
|
|
|
+ return handleResponse(response,
|
|
|
+ res -> {
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(res, typeReference);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ return (Result<T>) Result.error(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> Result<T> uploadWithJson(
|
|
|
+ String url,
|
|
|
+ MultipartFile file,
|
|
|
+ String json,
|
|
|
+ String authorization,
|
|
|
+ TypeReference<T> typeReference
|
|
|
+ ) {
|
|
|
+ try {
|
|
|
+ String boundary = UUID.randomUUID().toString();
|
|
|
+
|
|
|
+ Path tempFile = Files.createTempFile("upload-", file.getOriginalFilename());
|
|
|
+ file.transferTo(tempFile.toFile());
|
|
|
+
|
|
|
+ byte[] body = buildMultipart(
|
|
|
+ tempFile,
|
|
|
+ file.getOriginalFilename(),
|
|
|
+ json,
|
|
|
+ boundary
|
|
|
+ );
|
|
|
+
|
|
|
+ HttpRequest request = HttpRequest.newBuilder()
|
|
|
+ .uri(URI.create(url))
|
|
|
+ .header("Authorization", authorization)
|
|
|
+ .header("Content-Type", "multipart/form-data; boundary=" + boundary)
|
|
|
+ .POST(HttpRequest.BodyPublishers.ofByteArray(body))
|
|
|
+ .build();
|
|
|
+
|
|
|
+ HttpResponse<String> response =
|
|
|
+ httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
|
+
|
|
|
+ Files.deleteIfExists(tempFile);
|
|
|
+
|
|
|
+ return handleResponse(response,
|
|
|
+ res -> {
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(res, typeReference);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ return (Result<T>) Result.error(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private byte[] buildMultipart(
|
|
|
+ Path filePath,
|
|
|
+ String fileName,
|
|
|
+ String json,
|
|
|
+ String boundary
|
|
|
+ ) throws Exception {
|
|
|
+
|
|
|
+ String CRLF = "\r\n";
|
|
|
+ String prefix = "--" + boundary + CRLF;
|
|
|
+ String suffix = CRLF + "--" + boundary + "--";
|
|
|
+
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+
|
|
|
+ // ====== JSON Part ======
|
|
|
+ out.write(prefix.getBytes());
|
|
|
+ out.write(("Content-Disposition: form-data; name=\"metadata\"" + CRLF).getBytes());
|
|
|
+ out.write(("Content-Type: application/json" + CRLF + CRLF).getBytes());
|
|
|
+ out.write(json.getBytes(StandardCharsets.UTF_8));
|
|
|
+
|
|
|
+ // ====== File Part ======
|
|
|
+ out.write((CRLF + prefix).getBytes());
|
|
|
+ out.write(("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"" + CRLF).getBytes());
|
|
|
+ out.write(("Content-Type: application/octet-stream" + CRLF + CRLF).getBytes());
|
|
|
+ out.write(Files.readAllBytes(filePath));
|
|
|
+
|
|
|
+ // ====== End ======
|
|
|
+ out.write(suffix.getBytes());
|
|
|
+
|
|
|
+ return out.toByteArray();
|
|
|
+ }
|
|
|
+}
|