I have worked on SpringBoot rest api,
I have to convert below endpoints into AWS lambda function.
1) /service/promotion/ping/{pignId}. get single object of ping
2) /service/promotion/ping. get complete ping details
I have visited on AWS offical website for reference,
I had successfully converted first endpoint
/service/promotion/ping/{pignId}.
into AWS lambda function.
In first endpoint I was knew that {pignId} was coming in request param with type Integer so i have simply used AWS's RequestHandler interface to handler request like
public class CustomRequest implements RequestHandler<Integer, MyResponse> {
In second endpoint there is no request param means (Integer) coming in request, So
1) Which Handler should I used?
2)I want to send response code 200(OK) if endpoint works properly?
Thank you.
Related
I want to route requests based on some values in requestBody in spring cloud, for example:
if value of firstField in requestBody is chagre, I want to route this request to /chagre api
else if value of firstField in requestBody is package, I want to route this request to /package api
Any help would be appreciated.
Best practice is not to route according to request body, but use different attributes of the HTTP request instead. Spring Cloud Gateway includes many built-in route predicate factories based on those attributes.
The problem is request body can be read only once. moreover you need to know the object class it contains in order to properly read it.
In order to construct a solution to your question, we can use Spring Cloud Gateway ModifyRequestBody to rewrite the request body after you read it and before it send to the downstream.
Read more about ModifyRequestBody
My goal is to build a set of REST apis using aws api gateway, back with java Lambda functions and DynamoDB. The payload of the requests and responses is json. I want to avoid tedious field level mapping code between json and java and between java and dynamodb so am trying to use gson to map json to java objects the enhance dynamodb client so I can use java pojos as inputs and outputs of dynamodb queries. This all works fine when testing the functions stand alone.
The issue I am having is passing json between the Gateway and Lambda when trying to integrate gateway apis to the functions. I've tried the two interfaces of Lambda functions, RequestHandler and RequestStreamHandler. I can each of these to do half of what I need, but with RequestHander I have an get runtime errors when returning the response body as json, with RequestStreamHandler I can successfully set the response body as json, but I have problems receiving the request body as json.
It seems that something in the aws gateway framework is converting the body json into an escaped string and then another part of the framework is trying to convert the escaped string into json and failing.
What I am trying to do would seem the most basic REST api use case for aws gateway and Lambda, but I can't find any code examples in aws documentation beyond the trivial "hello world" type. Aws documentation seems pretty thin too, so has anyone out there been successfully in building REST apis with json payloads using aws gateway and Lambda who could share the secret of success?
Here's a bit of background as to what I've tried:
API Gateway:
I've created gateway apis using Lambda proxy integration to my Lambda functions. I've not specified any models to validate the input. I've just taken the default settings. I am passing a json object as the body of my HTTP requests to test the apis.
Lambda Functions
Coded in java using V2 of the SDK.
RequestHandler function - I have created a class the implements the RequestHandler interface specifying the input parameter as a HashMap and output as String. I have successfully got the body of the HTTP request from the hashmap and used Gson to convert the body to my Java pojo. All good.
However I fail trying to include json as the body of the response. I've used both Gson and org.json to construct my http response and include json as the body. I can unit test the function successfully, but when integrated with my gateway api I get an error in the gateway (not that the response has been escaped):
Tue Jan 12 11:12:23 UTC 2021 : Endpoint response body before transformations: "{"body":{"message":"hello world"},"isBase64encoded":false,"statusCode":200}"
Tue Jan 12 11:06:59 UTC 2021 : Execution failed due to configuration error: Malformed Lambda proxy response
RequestStreamHandler
The signature of the handleRequest method is:
public void handleRequest(InputStream input, OutputStream output, Context context)
In this case, I can successfully create a json response using gson and write it to the output stream and the api gateway is fine with it.
However I have a problem with the input. I have tried to use gson to parse the input stream and extract the body. This is fine, but the body is not json, it is an escaped json string, for example:
"{\r\n "id": "10",\r\n "title": "Ski trips",\r\n "owner": "captain",\r\n "locations": \r\n [\r\n {"latitude": 55, "longitude":-2 }\r\n ]\r\n}"
I can't use Gson to parse this nor use Gson to convert it to a pojo. If I try then I get runtime exceptions. To get round this I have used org.apache.commons.text.StringEscapeUtils to remove the escape characters and then removed the leading and trailing quotes round the string. I can then use Gson to convert the resultant string to my pojo class. This is tedious in the extreme.
I've tried changing the classes used for the inputs and outputs of the handleRequest methods, for example to return a JsonObject to see if that changed what I receive but to no avail. If the body contains just a string rather than json, then there is no problem. So just returning "hello world" as the body is fine as per the examples I've found.
I must be missing something, possible in the definition of the gateway api or in the types of the input and output parameters of the Lambda functions but without documentation it really is just wandering around in the dark. Hey all I want to do is to take a json object from the body of a rest request and write it as a json document to a nosql database, getting tripped up by conversion and mapping is really frustrating.
Thanks to anyone who can help me.
Lets say this is our Hander class with ApiGatewayRequest and ApiGatewayProxyResponse are request and response class names.
public class MyHandler implements RequestHandler<ApiGatewayRequest, ApiGatewayProxyResponse> {
#Override
public ApiGatewayProxyResponse handleRequest(ApiGatewayRequest request, Context context) {
}
}
Request Class
public class ApiGatewayRequest {
private String body;
private Map<String, String> headers;
private Map<String, String> queryStringParameters;
private Map<String, String> pathParameters;
private boolean isBase64Encoded;
}
Response Class
public class ApiGatewayProxyResponse {
private int statusCode;
private Map<String, String> headers;
private String body;
}
Json string should be sent in body with com.fasterxml.jackson.databind.ObjectMapper something like
String body = new ObjectMapper().writeValueAsString(ouputObject)
necessary headers ex: Access-Control-Allow-Credentials, Access-Control-Allow-Origin or Content-Type
I have solved my own problem. In aws gateway I had chosen my api to be of type Rest, which seemed the obvious choice for creating Rest services accessed over HTTP. I created a new api of type Http to integrate with the same Lambda function and to cut a long story short that solved the problem. The body of my post request as passed to the Lambda function is not escaped and can be process as json, and likewise the body in my response is not escaped or rejected before being returned to an http client. However I found this out through trial and error rather than by following clear documentation or working examples. My next step is to implement examples of get, post, put and delete HTTP methods to understand how parameters are passed in the case of path variables and query strings, as well as completing the integration with dynamodb. I'll post my working code once done.
Just started with using AWS Lambda's. I'm writing my functions in Java. I'd like to know if you can pass parameters to an AWS Lambda through the API Gateway? My lambda function basically makes a call to a webservice which returns JSON, create's POJO's from the JSON and then a CSV file which I upload to S3. Now this webservice you could pass productId if you wanted to, if you don't it just returns all products.
This would return the product with id of 123456
www.likssmark.com/test/api/getOrders?productId=123456
This would return all orders as JSON payload:
www.likssmark.com/test/api/getOrders
How do I pass productId into my java lambda? The lambda is triggered via cloud watch on a schedule - I've been testing it using Postman.
Hope this makes sense?
Many thanks for any advice.
If you only need to use Cloudwatch, just pass a JSON string to your Lambda:
In your Lambda you can then pull out the data:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
...
public class ProductIdLambda implements RequestStreamHandler {
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
LambdaLogger logger = context.getLogger();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(inputStream);
String productId = rootNode.path("productId").asInt();
This pulls the productId out of the InputStream.
If you need both CloudWatch events and API Gateway integration you can either have two different Lambda's or, to the suggestion #f7o made, introspect the incoming stream for an API Gateway call. You could have something like:
String httpMethod = rootNode.path("httpMethod").asText();
if( httpMethod != null ) // then we were called by API Gateway
The input from API Gateway will include an optional parameter in the input JSON:
"queryStringParameters": {
"productId": "12345"
},
that you can then get your productId from.
You can use AWS API-Gateway to pass parameters to your AWS lambda service.
This AWS documentation describes it: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html
In my German blog I wrote an article about how to implement an AWS lambda service with Java and Spring Boot. Here I am also passing parameters over API Gateway to AWS lambda service:
https://agile-coding.blogspot.com/2020/09/aws-lambda-services-mit-spring-boot.html
There are multiple ways to integrate lambda within an API gateway. For starters I would create a simple HTTP API with either a default route or a specific route. Attach a lambda integration to the route.
This should proxy the http request to your lambda function. You lambda handler will receive an event which contains information about the request as path, cookies, ... and also your query parameters. See the documentation for details on the passes json (https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html).
To determine who is actually calling the function (cloudwatch, api gateway) just test the content of the event for some fields before parsing/reading it to make sure you respond appropriate.
I'm sure there is an easy answer for this, but I'm only finding JS/Node examples on Amazon's site.
I'm trying to get the full request from the Gateway to pass to the AWS Lambda. I know you can build your own POJO, and I've got that working for a simple app. It looks like I really want to be using Lambda proxy integration, but I can't figure out what Amazon classes the RequestHandler expects for the Request and Response if I use the proxy integration.
My questions are:
Am I going down the right path for getting the complete request using the Lambda proxy integration?
What Java classes should I be using in the RequestHandler?
Thanks!
You can use the following handler structure:
#Override
class Handler implements RequestHandler<Map<String,Object>, Map<String,Object>> {
public Map<String,Object> handleRequest(Map<String,Object> input, Context context) {
// ...
}
}
Your response object should be a Map with
statusCode: HTTP response code (e.g., 200)
headers: a Map of HTTP headers (e.g., "Content-type"="application/json")
body: the actual response content (described by the Content-Type header)
I have developed a asynchronous JAX-RS web method using Apache CXF API. The webmethod takes a custom type as parameter as in
#POST
#Path("/query")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(value={MediaType.APPLICATION_JSON , "application/CustomType"})
public void getQueryResults(#Suspended final AsyncResponse asyncResponse, CustomType conf)
I had implemented(Service as well as Client side) a Custom MessageBodyReader and MessageBodyWriter to take care of serializing my 'CustomType'.
On the Client side i ergister the imlpemented ones as
Client client = ClientBuilder.newClient().register(CacheConfigReader.class).register(CacheConfigWriter.class);
I make a async request to the service that has
asyncResponse.resume(result); // result is a string
On the client
Future<String> future = asyncInvoker.post(entity, String.class);
My observation is that randomly the response is empty though on the server logs am able to see non-empty result. upon debugging i find that there are two threads that invoke
JaxrsClientCallback . handleResponse()
One of them with the actual result and another empty. Based on what executes first the result is the actual string or empty. The trace of the call contains invocation from phase interceptor chain.
This occurs only when i register the client with Custom reader and writers. When I set the request body with a json only one thread handles the response.
Can someone shed light on why the addition of MessageBodyReaders / Writers causes this issue ?