AWS Lambda - Java 个豆类
AWS Lambda - Java Beans
我有一个如下所示的请求:
package pricing
import scala.beans.BeanProperty
class Request(@BeanProperty var name: String, @BeanProperty var surname: String) {
def this() = this(name="defName", surname="defSurname")
}
处理程序如下:
package pricing
import com.amazonaws.services.lambda.runtime.{Context, RequestHandler}
import scala.collection.JavaConverters
import spray.json._
class ApiGatewayHandler extends RequestHandler[Request, ApiGatewayResponse] {
import DefaultJsonProtocol._
def handleRequest(input: Request, context: Context): ApiGatewayResponse = {
val headers = Map("x-foo" -> "coucou")
val msg = "Hello " + input.name
val message = Map[String, String]("message" -> msg )
ApiGatewayResponse(
200,
message.toJson.toString(),
JavaConverters.mapAsJavaMap[String, Object](headers),
true
)
}
}
已记录为:
functions:
pricing:
handler: pricing.ApiGatewayHandler
events:
- http:
path: pricing/test
method: get
documentation:
summary: "submit your name and surname, the API says hi"
description: ".. well, the summary is pretty exhaustive"
requestBody:
description: "Send over name and surname"
queryParams:
- name: "name"
description: "your 1st name"
- name: "surname"
description: ".. guess .. "
methodResponses:
- statusCode: "200"
responseHeaders:
- name: "x-foo"
description: "you can foo in here"
responseBody:
description: "You'll see a funny message here"
responseModels:
"application/json": "HelloWorldResponse"
好吧,这是其中一个教程的复制和粘贴。它不起作用。
我猜 BeanProperty
指的是 body 对象属性;这就是我从示例 here
.
中可以猜到的
如果我想要查询字符串?
一个尝试是:
package pricing
import scala.beans.BeanProperty
import spray.json._
abstract class ApiGatewayGetRequest(
@BeanProperty httpMethod: String,
@BeanProperty headers: Map[String, String],
@BeanProperty queryStringParameters: Map[String, String])
abstract class ApiGatewayPostRequest(
@BeanProperty httpMethod: String,
@BeanProperty headers: Map[String, String],
@BeanProperty queryStringParameters: Map[String, String])
class HelloWorldRequest(
@BeanProperty httpMethod: String,
@BeanProperty headers: Map[String, String],
@BeanProperty queryStringParameters: Map[String, String]
) extends ApiGatewayGetRequest(httpMethod, headers, queryStringParameters) {
private def getParam(param: String): String =
queryStringParameters get param match {
case Some(s) => s
case None => "default_" + param
}
def name: String = getParam("name")
def surname: String = getParam("surname")
def this() = this("GET", Map.empty, Map.empty)
}
这导致:
{
"message":"Hello default_name"
}
建议 class 已用空地图代替 queryStringParameters
初始化,但已正确提交
Mon Sep 25 20:45:22 UTC 2017 : Endpoint request body after
transformations:
{"resource":"/pricing/test","path":"/pricing/test","httpMethod":"GET","headers":null,"queryStringParameters":{"name":"ciao", "surname":"bonjour"},"pathParameters":null,"stageVariables":null,
...
注意:
我走这条路是因为我觉得用类型 T 替换 @BeanProperty queryStringParameters: Map[String, String]
中的 Map
会方便且富有表现力,例如
case class Person(@beanProperty val name: String, @beanProperty val surname: String)
但是,上面的代码将 {"name":"ciao", "surname":"bonjour"}
视为 String
,但没有弄清楚它应该反序列化该字符串。
编辑
我也尝试用 java.util.Map[String, String]
替换 scala 映射但没有成功
默认情况下,Serverless 启用 proxy integration between the lambda and API Gateway。这对您来说意味着 API Gateway 将把一个包含关于请求的所有元数据的对象传递给您的处理程序,正如您已经注意到的:
Mon Sep 25 20:45:22 UTC 2017 : Endpoint request body after transformations: {"resource":"/pricing/test","path":"/pricing/test","httpMethod":"GET","headers":null,"queryStringParameters":{"name":"ciao", "surname":"bonjour"},"pathParameters":null,"stageVariables":null, ...
这显然不会映射到您的模型,其中只有字段 name
和 surname
。有几种方法可以解决这个问题。
1。调整您的模型
如果您通过使字段可变(并因此为它们创建设置器)使 class 成为合适的 POJO,您对 HelloWorldRequest
class 的尝试确实有效:
class HelloWorldRequest(
@BeanProperty var httpMethod: String,
@BeanProperty var headers: java.util.Map[String, String],
@BeanProperty var queryStringParameters: java.util.Map[String, String]
) extends ApiGatewayGetRequest(httpMethod, headers, queryStringParameters) {
AWS Lambda 文档states:
The get and set methods are required in order for the POJOs to work with AWS Lambda's built in JSON serializer.
另请注意,不支持 Scala 的 Map。
2。使用自定义请求模板
如果您不需要元数据,那么您可以使用 mapping templates.
让 API 网关仅将您需要的数据传递到 lambda 中,而不是更改您的模型
为此,您需要告诉 Serverless 使用普通的 lambda 集成(而不是代理)和 specify a custom request template。
Amazon API 网关文档有 an example request template 几乎可以完美解决您的问题。稍微调整一下,我们得到
functions:
pricing:
handler: pricing.ApiGatewayHandler
events:
- http:
path: pricing/test
method: get
integration: lambda
request:
template:
application/json: |
#set($params = $input.params().querystring)
{
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
此模板将从查询字符串参数中生成 JSON,现在它将成为 lambda 的输入:
Endpoint request body after transformations: { "name" : "ciao" }
哪个正确映射到您的模型。
请注意,禁用代理集成也会更改响应格式。您会注意到现在您的 API return 直接是您的响应模型:
{"statusCode":200,"body":"{\"message\":\"Hello ciao\"}","headers":{"x-foo":"coucou"},"base64Encoded":true}
您可以通过将代码修改为仅 return 正文或添加自定义响应模板来解决此问题:
response:
template: $input.path('$.body')
这会将输出转换为您所期望的,但会公然忽略 statusCode
和 headers
。您将需要进行更复杂的响应配置来处理这些问题。
3。自己做映射
而不是扩展 RequestHandler
并让 AWS Lambda 将 JSON 映射到 POJO,you can instead extend RequestStreamHandler
,这将为您提供 InputStream
和 OutputStream
,因此您可以使用您选择的 JSON 序列化程序进行(反)序列化。
我有一个如下所示的请求:
package pricing
import scala.beans.BeanProperty
class Request(@BeanProperty var name: String, @BeanProperty var surname: String) {
def this() = this(name="defName", surname="defSurname")
}
处理程序如下:
package pricing
import com.amazonaws.services.lambda.runtime.{Context, RequestHandler}
import scala.collection.JavaConverters
import spray.json._
class ApiGatewayHandler extends RequestHandler[Request, ApiGatewayResponse] {
import DefaultJsonProtocol._
def handleRequest(input: Request, context: Context): ApiGatewayResponse = {
val headers = Map("x-foo" -> "coucou")
val msg = "Hello " + input.name
val message = Map[String, String]("message" -> msg )
ApiGatewayResponse(
200,
message.toJson.toString(),
JavaConverters.mapAsJavaMap[String, Object](headers),
true
)
}
}
已记录为:
functions:
pricing:
handler: pricing.ApiGatewayHandler
events:
- http:
path: pricing/test
method: get
documentation:
summary: "submit your name and surname, the API says hi"
description: ".. well, the summary is pretty exhaustive"
requestBody:
description: "Send over name and surname"
queryParams:
- name: "name"
description: "your 1st name"
- name: "surname"
description: ".. guess .. "
methodResponses:
- statusCode: "200"
responseHeaders:
- name: "x-foo"
description: "you can foo in here"
responseBody:
description: "You'll see a funny message here"
responseModels:
"application/json": "HelloWorldResponse"
好吧,这是其中一个教程的复制和粘贴。它不起作用。
我猜 BeanProperty
指的是 body 对象属性;这就是我从示例 here
.
如果我想要查询字符串?
一个尝试是:
package pricing
import scala.beans.BeanProperty
import spray.json._
abstract class ApiGatewayGetRequest(
@BeanProperty httpMethod: String,
@BeanProperty headers: Map[String, String],
@BeanProperty queryStringParameters: Map[String, String])
abstract class ApiGatewayPostRequest(
@BeanProperty httpMethod: String,
@BeanProperty headers: Map[String, String],
@BeanProperty queryStringParameters: Map[String, String])
class HelloWorldRequest(
@BeanProperty httpMethod: String,
@BeanProperty headers: Map[String, String],
@BeanProperty queryStringParameters: Map[String, String]
) extends ApiGatewayGetRequest(httpMethod, headers, queryStringParameters) {
private def getParam(param: String): String =
queryStringParameters get param match {
case Some(s) => s
case None => "default_" + param
}
def name: String = getParam("name")
def surname: String = getParam("surname")
def this() = this("GET", Map.empty, Map.empty)
}
这导致:
{
"message":"Hello default_name"
}
建议 class 已用空地图代替 queryStringParameters
初始化,但已正确提交
Mon Sep 25 20:45:22 UTC 2017 : Endpoint request body after
transformations:
{"resource":"/pricing/test","path":"/pricing/test","httpMethod":"GET","headers":null,"queryStringParameters":{"name":"ciao", "surname":"bonjour"},"pathParameters":null,"stageVariables":null,
...
注意:
我走这条路是因为我觉得用类型 T 替换 @BeanProperty queryStringParameters: Map[String, String]
中的 Map
会方便且富有表现力,例如
case class Person(@beanProperty val name: String, @beanProperty val surname: String)
但是,上面的代码将 {"name":"ciao", "surname":"bonjour"}
视为 String
,但没有弄清楚它应该反序列化该字符串。
编辑
我也尝试用 java.util.Map[String, String]
替换 scala 映射但没有成功
默认情况下,Serverless 启用 proxy integration between the lambda and API Gateway。这对您来说意味着 API Gateway 将把一个包含关于请求的所有元数据的对象传递给您的处理程序,正如您已经注意到的:
Mon Sep 25 20:45:22 UTC 2017 : Endpoint request body after transformations: {"resource":"/pricing/test","path":"/pricing/test","httpMethod":"GET","headers":null,"queryStringParameters":{"name":"ciao", "surname":"bonjour"},"pathParameters":null,"stageVariables":null, ...
这显然不会映射到您的模型,其中只有字段 name
和 surname
。有几种方法可以解决这个问题。
1。调整您的模型
如果您通过使字段可变(并因此为它们创建设置器)使 class 成为合适的 POJO,您对 HelloWorldRequest
class 的尝试确实有效:
class HelloWorldRequest(
@BeanProperty var httpMethod: String,
@BeanProperty var headers: java.util.Map[String, String],
@BeanProperty var queryStringParameters: java.util.Map[String, String]
) extends ApiGatewayGetRequest(httpMethod, headers, queryStringParameters) {
AWS Lambda 文档states:
The get and set methods are required in order for the POJOs to work with AWS Lambda's built in JSON serializer.
另请注意,不支持 Scala 的 Map。
2。使用自定义请求模板
如果您不需要元数据,那么您可以使用 mapping templates.
让 API 网关仅将您需要的数据传递到 lambda 中,而不是更改您的模型为此,您需要告诉 Serverless 使用普通的 lambda 集成(而不是代理)和 specify a custom request template。
Amazon API 网关文档有 an example request template 几乎可以完美解决您的问题。稍微调整一下,我们得到
functions:
pricing:
handler: pricing.ApiGatewayHandler
events:
- http:
path: pricing/test
method: get
integration: lambda
request:
template:
application/json: |
#set($params = $input.params().querystring)
{
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
此模板将从查询字符串参数中生成 JSON,现在它将成为 lambda 的输入:
Endpoint request body after transformations: { "name" : "ciao" }
哪个正确映射到您的模型。
请注意,禁用代理集成也会更改响应格式。您会注意到现在您的 API return 直接是您的响应模型:
{"statusCode":200,"body":"{\"message\":\"Hello ciao\"}","headers":{"x-foo":"coucou"},"base64Encoded":true}
您可以通过将代码修改为仅 return 正文或添加自定义响应模板来解决此问题:
response:
template: $input.path('$.body')
这会将输出转换为您所期望的,但会公然忽略 statusCode
和 headers
。您将需要进行更复杂的响应配置来处理这些问题。
3。自己做映射
而不是扩展 RequestHandler
并让 AWS Lambda 将 JSON 映射到 POJO,you can instead extend RequestStreamHandler
,这将为您提供 InputStream
和 OutputStream
,因此您可以使用您选择的 JSON 序列化程序进行(反)序列化。