您可以使用Java請求處理程序響應接收到的事件并執行相應的業務邏輯。本文介紹Java請求處理程序的相關概念、結構特點和示例。
如您需要通過HTTP觸發器或自定義域名訪問函數,請先獲取請求結構體再自定義HTTP響應。更多信息,請參見HTTP觸發器調用函數。
什么是請求處理程序
FC函數的請求處理程序,是函數代碼中處理請求的方法。當您的函數被調用時,函數計算會運行您提供的Handler方法處理請求。
您可以通過函數計算控制臺配置請求處理程序,對于Java語言的函數,您的請求處理程序需配置為[包名].[類名]::[方法名]
。例如,您的包名為example,類型為HelloFC,方法名為handleRequest,則請求處理程序可配置為example.HelloFC::handleRequest
。
請求處理程序的具體配置均需符合函數計算平臺的配置規范。配置規范因請求處理程序類型而異。
處理程序接口
您在使用Java編程時,必須要實現函數計算提供的接口類,fc-java-core庫為請求處理程序定義了以下兩個接口。
以流的方式接收輸入的
event
事件并返回執行結果。您需要從輸入流中讀取調用函數時的輸入,處理完成后把函數執行結果寫到輸出流中來返回。以泛型的方式接收輸入的
event
事件并返回執行結果。您可以自定義輸入和輸出的類型,但是輸入和輸出的類型必須是POJO類型。
StreamRequestHandler
一個最簡單的StreamRequestHandler示例如下所示。
package example;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.StreamRequestHandler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class HelloFC implements StreamRequestHandler {
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
outputStream.write(new String("hello world").getBytes());
}
}
包名和類名
由于Java有包的概念,因此執行方法和其他語言有所不同,需要包含包的信息。代碼示例中請求處理程序(handler)為
example.HelloFC::handleRequest
,其中example
標識為包名,HelloFC
標識為類名,handleRequest
標識為類方法。說明包名和類名可以是任意的,但是需要與函數配置信息中的請求處理程序字段相對應。關于請求處理程序的設置,請參見創建事件函數。
實現的接口
您的代碼中必須要實現函數計算預定義的接口。上述的代碼示例中實現了
StreamRequestHandler
,其中的inputStream參數為調用函數時傳入的數據,outputStream參數用于返回函數的執行結果。Context參數
Context參數中包含一些函數的運行時信息(例如RequestId、臨時AccessKey等),其類型是
com.aliyun.fc.runtime.Context
。具體信息,請參見上下文。返回值
實現
StreamRequestHandler
接口的函數通過outputStream
參數返回執行結果。引入接口庫
其中用到的
com.aliyun.fc.runtime
包的依賴可以通過下文的pom.xml
引用。<dependency> <groupId>com.aliyun.fc.runtime</groupId> <artifactId>fc-java-core</artifactId> <version>1.4.1</version> </dependency>
您可以通過Maven倉庫獲取
fc-java-core
最新的版本號。
在創建函數之前,您需要將代碼和其依賴的fc-java-core
打包為JAR格式的壓縮包。打包方式,請參見編譯部署代碼包。
PojoRequestHandler
一個最簡單的PojoRequestHandler示例如下所示。SimpleRequest的對象需要支持JSON序列化,例如POJO。
// HelloFC.java
package example;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.PojoRequestHandler;
public class HelloFC implements PojoRequestHandler<SimpleRequest, SimpleResponse> {
@Override
public SimpleResponse handleRequest(SimpleRequest request, Context context) {
String message = "Hello, " + request.getFirstName() + " " + request.getLastName();
return new SimpleResponse(message);
}
}
// SimpleRequest.java
package example;
public class SimpleRequest {
String firstName;
String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public SimpleRequest() {}
public SimpleRequest(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
// SimpleResponse.java
package example;
public class SimpleResponse {
String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SimpleResponse() {}
public SimpleResponse(String message) {
this.message = message;
}
}
傳入的event參數示例如下。
{
"firstName": "FC",
"lastName": "aliyun"
}
示例:使用HTTP觸發器調用函數
示例代碼
以上示例代碼中,HTTPTriggerEvent.java聲明了HTTP觸發器的請求格式,HTTPTriggerResponse.java聲明了HTTP觸發器的響應格式,關于HTTP觸發器的請求結構體和響應結構體的詳細格式,請參見HTTP觸發器調用函數。
package example;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(setterPrefix = "with")
public class HTTPTriggerEvent {
private String version;
private String rawPath;
private Map<String, String> headers;
private Map<String, String> queryParameters;
private RequestContext requestContext;
@JsonProperty("isBase64Encoded")
private boolean IsBase64Encoded;
private String body;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(setterPrefix = "with")
public static class RequestContext {
private String accountId;
private String domainName;
private String domainPrefix;
private HttpInfo http;
private String requestId;
private String time;
private String timeEpoch;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(setterPrefix = "with")
public static class HttpInfo {
private String method;
private String path;
private String protocol;
private String sourceIp;
private String userAgent;
}
}
}
package example;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(setterPrefix = "with")
public class HTTPTriggerResponse {
private int statusCode;
private Map<String, String> headers;
@JsonProperty("isBase64Encoded")
private boolean IsBase64Encoded;
private String body;
}
App.java定義了函數入口類。
package example;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.PojoRequestHandler;
/**
* HTTP Trigger demo
*
*/
public class App implements PojoRequestHandler<HTTPTriggerEvent, HTTPTriggerResponse> {
@Override
public HTTPTriggerResponse handleRequest(HTTPTriggerEvent request, Context context) {
context.getLogger().info("Receive HTTP Trigger request: " + request.toString());
String requestBody = request.getBody();
if (request.isIsBase64Encoded()) {
requestBody = new String(java.util.Base64.getDecoder().decode(request.getBody()), StandardCharsets.UTF_8);
}
String message = "HTTP Trigger request body: " + requestBody;
context.getLogger().info(message);
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "text/plain");
return HTTPTriggerResponse.builder().withStatusCode(200).withHeaders(headers).withBody(request.getBody())
.withIsBase64Encoded(request.isIsBase64Encoded()).build();
}
}
本示例程序除了引入fc-java-core庫外,還需要引入jackson和lombok兩個庫,可以在Maven的配置文件pom.xml
中添加此依賴。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
前提條件
已使用上述示例創建運行環境為Java的函數,并創建HTTP觸發器。具體操作,請參見創建事件函數和配置HTTP觸發器并使用HTTP觸發。
操作步驟
登錄函數計算控制臺,在左側導航欄,單擊函數。
在頂部菜單欄,選擇地域,然后在函數頁面,單擊目標函數。
在函數詳情頁面,單擊觸發器管理頁簽,獲取HTTP觸發器的公網訪問地址。
在Curl工具執行以下命令,調用函數。
curl -i "https://dev-jav-test-fc-luiqas****.cn-shanghai.fcapp.run" -d 'hello fc3.0'
以上命令中,
https://dev-jav-test-fc-luiqas****.cn-shanghai.fcapp.run
為獲取到的HTTP觸發器公網訪問地址。響應結果如下。
HTTP/1.1 200 OK Content-Disposition: attachment Content-Length: 11 Content-Type: application/json X-Fc-Request-Id: 1-652503f2-afbfd2b1dc4dd0fcb0230959 Date: Tue, 10 Oct 2023 07:57:38 GMT hello fc3.0%
錯誤分析
本示例代碼支持使用HTTP觸發器或者自定義域名調用。如果使用API調用,但配置的測試參數不符合HTTP觸發器請求格式規范時,會出現報錯。
例如,在控制臺上調用函數,配置請求參數為"Hello, FC!"
,單擊測試函數,收到的響應如下所示。
{
"errorType": "com.fasterxml.jackson.databind.exc.MismatchedInputException",
"errorMessage": "Cannot construct instance of `example.HTTPTriggerEvent` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('Hello, FC!')\n at [Source: (byte[])\"\"Hello, FC!\"\"; line: 1, column: 1]",
"stackTrace": [
"com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)",
"com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1588)",
"com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1213)",
"com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromString(StdDeserializer.java:311)",
"com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1495)",
"com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:207)",
"com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:197)",
"com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)",
"com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)",
"com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3643)"
]
}
示例程序
函數計算官方庫包含了使用各種處理程序類型和接口的示例應用程序。每個示例應用程序都包含用于輕松編譯部署的方法。例如:
java11-blank-stream-event:使用Stream格式的事件回調處理程序。
java11-blank-pojo-event:使用POJO格式的事件回調處理程序。