供程式串接用的網頁服務 (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());
}
}
- 參考資料
- 對應各 Java/Jakarta EE 版本,與其相容的 GlassFish, JDK 版本可參考下址
Version history of Java EE, JSF, GlassFish, JDK, and NetBeans
沒有留言:
張貼留言