how to write Java HTTP client and server for RESTful web services

供程式串接用的網頁服務 (Web Service) 分成客戶端及服務端。以 Java RESTful Web Service 為例,假設服務端點規格如下:

    http://example.com:8080/api/v1/users/123?name=John 

其中,本體區皆以 JSON 物件字串為資料交換標準,輸入參數用到路徑123,查詢字串name=John,標頭區Area: Taipei,本體區phone: 123-4567。其 POST 請求 (Request) 及回應 (Response) 如下,

Request Header and Body
POST /api/users/123?name=John HTTP/1.1
Host: example.com:8080
Accept: application/json
Area: Taipei

{ "phone": "123-4567" }
Response Header and Body
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 85

{
  "id": 123,
  "name": "John",
  "area": "Taipei",
  "phone": "123-4567"
}

以下介紹上述端點規格的服務端及客戶端寫法:

(一) 服務端寫法,將使用 JAX-RS Jersey 套件

A. Java EE 7 手動轉型寫法如下,須搭配 GlassFish 4.1.2 (Java EE 7) with JDK 8

// Old Version:
//  On NetBeans, use Tools/Server 
//    to install GlassFish 4.1.2 (Java EE 7) with JDK 8
//  Use New/RESTful Web Services from Patterns...
//    to set up ApplicationConfig.java and Resource.java
//
import javax.ws.rs.Consumes;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
//import javax.ws.rs.core.Response;
import com.goolge.gson.Gson;  // install gson-2.7.jar in library

@Path("/api/users")
public class UserResource {

    @POST
    @Path("{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String updateUser(      // 此方法名無關緊要,回傳型別建議用字串
            @PathParam("id") int id,
            @QueryParam("name") String name,
            @HeaderParam("Area") String area,
            String requestBody) {  // 請求本體區建議用字串接收,避免自動轉型出錯

        // 取出請求本體區物件
        PhoneRequest request = new Gson()
            .fromJson(requestBody, PhoneRequest.class); // 字串轉物件

        // 依據給定 id, name, area, request,執行運算邏輯

        // 建構回應物件,轉型成字串回應
        UserResponse response = new UserResponse();
        response.id = id;
        response.name = name;
        response.area = area;
        response.phone = request.phone;

        String jsonResponse = new Gson().toJson(response);  // 物件轉字串
        return jsonResponse;  // 回傳用字串

        //return Response.ok(response).build();  // 回傳用物件,可能報錯
    }

    // Request body class 請求本體物件
    public static class PhoneRequest {

        public String phone;
    }

    // Response body class 回應本體物件
    public static class UserResponse {

        public int id;
        public String name;
        public String area;
        public String phone;
    }
}

B. Jakarta EE 9.1 自動轉型寫法如下,須搭配 GlassFish 6.2.5 (Jakarta EE 9.1) with JDK 11

// New Version:
//  On NetBeans, use Tools/Server 
//    to install GlassFish 6.2.5 (Jakarta EE 9.1) with JDK 11
//  Use New/RESTful Web Services from Patterns...
//    to set up ApplicationConfig.java and Resource.java
//
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

@Path("/api/users")
public class UserResource {

    @POST
    @Path("{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response updateUser(      // 此方法名無關緊要,回傳型別 Response
            @PathParam("id") int id,
            @QueryParam("name") String name,
            @HeaderParam("Area") String area,
            PhoneRequest requestBody) {  // 請求本體區物件型別 PhoneRequest

        // 依據給定 id, name, area, requestBody,執行運算邏輯

        // 建構回應物件
        UserResponse response = new UserResponse();
        response.id = id;
        response.name = name;
        response.area = area;
        response.phone = requestBody.phone;

        return Response.ok(response).build();  // 回傳成功狀態 Response 物件
        // Response.status(Response.Status.BAD_REQUEST) // 其他錯誤狀態回傳法
        //       .entity("Invalid input")
        //       .build();
    }

    // Request body class 請求本體物件
    public static class PhoneRequest {

        public String phone;
    }

    // Response body class 回應本體物件
    public static class UserResponse {

        public int id;
        public String name;
        public String area;
        public String phone;
    }
}

(二) 至於客戶端有如下三種常用寫法,可供比較選用:

  • JAX-RS 適合用於 Java/Jakarta EE 或 Jersey 等 RESTful 應用。
  • HttpURLConnection 是最基本的方式,無需額外套件,但較繁瑣。
  • HttpClient (JDK 11+) 是現代化且功能強大的選擇,須 JDK11以上才支援。

 A.使用 JAX-RS Client API

// On NetBeans, use New/RESTful Java Client... 
//   to add JAX-RS libraries which support javax/jakarta.ws.rs.* packages
//   and set up NewJerseyClient.java
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

public class JaxRsClientExample {
    public static void main(String[] args) {
        Client client = ClientBuilder.newClient();
        WebTarget target = client
            .target("http://example.com:8080/api/users/123")
            .queryParam("name", "John");

        Invocation.Builder builder = target
            .request(MediaType.APPLICATION_JSON)
            .header("Area", "Taipei");

        String jsonBody = "{ \"phone\": \"123-4567\" }";

        Response response = builder.post(
            Entity.entity(jsonBody, MediaType.APPLICATION_JSON));

        String responseBody = response.readEntity(String.class);
        System.out.println("Response: " + responseBody);
        response.close();
        client.close();
    }
}

B.使用 java.net.HttpURLConnection

import java.io.BufferedReader;
import java.io.IOException;  // thrown by httpURLConnection.openConnection()
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException; // thrown by URL()
import java.net.URL;

public class HttpURLConnectionExample {
    public static void main(String[] args) throws IOException, MalformedURLException {
        URL url = new URL("http://example.com:8080/api/users/123?name=John");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        conn.setRequestMethod("POST");
        conn.setRequestProperty("Accept", "application/json");
        conn.setRequestProperty("Area", "Taipei");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setDoOutput(true);  // needed before writing the request body

        String jsonInput = "{ \"phone\": \"123-4567\" }";
        // use try with resource block without catch block
        //   output stream will be auto closed when leaving the block
        //   any exception in the block will be passed upward to the calling method
        try (OutputStream os = conn.getOutputStream()) {
            os.write(jsonInput.getBytes());
            os.flush();
        }

        BufferedReader br = new BufferedReader(
            new InputStreamReader(conn.getInputStream()));
        String line;
        StringBuilder response = new StringBuilder();
        while ((line = br.readLine()) != null) {
            response.append(line);
        }

        System.out.println("Response: " + response.toString());
        conn.disconnect();
    }
}

C.使用 java.net.http.HttpClient (JDK 11+)

// JDK 11 above is required
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;

public class HttpClientExample {
    public static void main(String[] args) throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("http://example.com:8080/api/users/123?name=John"))
            .header("Accept", "application/json")
            .header("Area", "Taipei")
            .header("Content-Type", "application/json")
            .POST(BodyPublishers.ofString("{ \"phone\": \"123-4567\" }"))
            .build();

        HttpResponse<String> response = client.send(request, BodyHandlers.ofString());

        System.out.println("Response: " + response.body());
    }
}
    參考資料
  1. 對應各 Java/Jakarta EE 版本,與其相容的 GlassFish, JDK 版本可參考下址
    Version history of Java EE, JSF, GlassFish, JDK, and NetBeans

沒有留言:

how to install and use Jeddict AI Assistant on NetBeans IDE 23+

Jeddict AI Assistant  為適用於  NetBeans IDE 23 以上版本的插件,能夠連接大語言模型,協助編寫程式碼,大幅提升開發體驗,讓程式撰寫更輕鬆、更智慧。以下簡介其安裝方法及功能。 A.安裝與解除安裝 安裝步驟: ...

總網頁瀏覽量