Spring Cloud Function with Function Routing in AWS Lambda using API Gateway - aws-lambda

I have tried to deploy a Spring Cloud Function with multiple functions in AWS Lambda.
For HTTP access I have created an HTTP API Gateway (not a REST API). I wanted to use function routing as described here: https://cloud.spring.io/spring-cloud-static/spring-cloud-function/3.0.1.RELEASE/reference/html/spring-cloud-function.html#_function_routing.
With the following configuration, the RoutingFunction shoud deleagte the call to the correct function based on the function HTTP-Header:
spring.cloud.function.routing-expression=headers.function
The Lambda Handler class is:
org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler
and the configured function name (FUNCTION_NAME) is: functionRouter.
When I send a Request to the API-Gateway, the FunctionRouter gets a FluxJust object instead of a Message object, because the RequestHandler seems to be a Publisher.
So I get the following exception:
EL1008E: Property or field 'headers' cannot be found on object of type
'reactor.core.publisher.FluxJust' - maybe not public or not valid?:
org.springframework.expression.spel.SpelEvaluationException
org.springframework.expression.spel.SpelEvaluationException:
EL1008E: Property or field 'headers' cannot be found on object of type
'reactor.core.publisher.FluxJust' - maybe not public or not valid?
My current workaround is to intercept the request before the request is delegated to the RoutingFunction and try to reconstruct the payload from the HashMap with the following code:
#Autowired
RoutingFunction f;
#Bean
public Function<Message<LinkedHashMap>,?> router() {
return value -> {
String json = new JSONObject(value.getPayload()).toString();
Message<?> m = MessageBuilder.createMessage(json, value.getHeaders());
Object result = f.apply(m);
return result;
};
}
Is there a way proper way to use the HTTP API Gateway in combination with Function Routing in AWS?

On my project I've set function.definition and function.routing-expression like that:
spring.cloud.function.definition=functionRouter
spring.cloud.function.routing-expression=headers['pathParameters']['proxy']
This will be using the org.springframework.cloud.function.context.config.RoutingFunction that Spring Cloud Function provides and the expression would get the function name from the path.
If you still want to use the headers you could do:
spring.cloud.function.routing-expression=headers['headers']['function'].
The HTTP headers are added in the Message headers in the headers property.
So first we get the Message headers and then the HTTP headers and then the header key.

Related

async Lambda invocation from Api Gatway with parameters

I have an Api Gateway GET method called /tasks/{tasktype}
It's pointed to a Lambda function with the X-Amz-Invocation-Type set to 'Event'
Then in my Lambda I have this
public void FunctionHandler(Object input, ILambdaContext context)
{
LambdaLogger.Log($"GOT: {input.ToString()}");
}
This all works fine, except input is null.
Is there any way I can pass through and access the value of {tasktype} from the Api Gateway?
Thanks
You need to pass them in using either a mapping template or check the box for "Use Lambda Proxy integration".
Mapping Template reference:
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
Proxy Integration reference:
https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html

Spring Rest Service Return Type

I have a rest service in spring which can return a string or a json. For this in my js code while sending the ajax request, i have specified datatype as "*". I wanted to know how can i handle this in spring service
All produces type are available in org.springframework.http.MediaType and for your requirement you can pass */*.
Constant for that is MediaType.ALL_VALUE in java code.
But if you know that your service always return JSON then I prefer to use MediaType.APPLICATION_JSON_UTF8_VALUE instead of MediaType.ALL_VALUE.
you can add attribute " produces" on your RequestMapping annotation:
#RequestMapping(value = "/yourPath", method = GET,
produces = { "application/json", "application/xml",....all what you want as type})

Swagger TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body

I have added Swagger to my Spring Boot 2 application:
This is my Swagger config:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
// #formatter:off
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
// #formatter:on
}
}
This is Maven dependency:
<!-- Swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
When I try to invoke for example http://localhost:8080/api/actuator/auditevents it fails with the following error:
TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
What am I doing wrong and how to fix it ?
I ran into this issue. Here is how I resolved it:
I had a method like this:
[HttpGet]
public IEnumerable<MyObject> Get(MyObject dto)
{
...
}
and I was getting the error. I believe swagger UI is interpreting the Get parameters as FromBody, so it uses the curl -d flag. I added the [FromQuery] decorator and the problem was resolved:
[HttpGet]
public IEnumerable<MyObject> Get([FromQuery]MyObject dto)
{
...
}
FYI this also changes the UI experience for that method. instead of supplying json, you will have a form field for each property of the parameter object.
The error message actually says what the problem is. You post data with curl using the -d option while trying to use GET.
If you use the -d option curl will do POST.
If you use -X GET option curl will do GET.
The HTTP GET method is for requesting a representation of the specified resource. Requests using GET should only retrieve data and hence cannot have body.
More info on GET vs POST
I had same problem with my .net core 2.0 solution and GET method that takes element id as header key or search for it by parameters in body. That is not the best way to implement but it's kind of special case.
As mentioned in this discussion. The HTTP spec does not forbid using body on a GET, but swagger is not implementing it like this. Even if there are APIs that work fine with body in GET Requests.
What more, the swagger frontend adds this body object into request even if it is null/undefined/empty object. It is -d "body_content_here" parameter. So in my case when i only search by id and body is empty, it still sends empty object (-d "{}") and throws mentioned error.
Possible solutions:
Start using postman app for this request - It will work fine. Tested.
Consider moving more advanced GET request (like search with criteria) to the independent POST Method
Use swagger generated CURL request request without -d parameter
Don't pass method type in Get method.
let res = await fetch("http://localhost:8080/employee_async",{
method: "POST",
body:JSON.stringify(data),
mode:"cors",
headers: {"Content-type":"application/json;charset=utf-8"}})
This is used for post only, If we don't assign any method type node automatically considered as Get method
To avoid this error be sure to annotate parameters in your controller with #RequestParam, like
#GetMapping("/get")
public Response getData(#RequestParam String param)
Looking at swagger exception/error message , looks like you are calling Get method with a set of input body. As per documentation of GET method doesn't accept any body. You need to change the GET method to POST method. It should work.
Maybe the problem is with the mapping of the method, make sure to use
#RequestMapping(value = "/<your path>" , method = RequestMethod.POST)
and put the data as body with #RequestBody
I also got the same error on the Swagger UI. My problem was I have mistakenly marked the Api Method as GET and send data in the request body. Once I change the annotation #GET to #POST issue got resolved.
Because you used GET http method with body.
If you want to have Json body, etc you need to use POST http method,
For example in your controller class, top of your method:
#PostMapping(value = "/save")
public ResponseEntity<HttpStatus> savePerson(#RequestBody Person person)
{...}
Use GET without body.
Pass Paremeters with [FromQuery] in Methods InPut
I was having this issue when trying to use Swagger UI on a Ruby On Rails app. I fixed it by changing the information container on the curl command. This is a example line:
parameter name: :body, in: :body, schema: {'$ref' => '#/definitions/ActivitiesFilter'}, required: true
into
parameter name: :attribute_name1, in: :query, required: true
parameter name: :attribute_name2, in: :query, required: true
parameter name: :attribute_name3, in: :query, required: true
note that you have to add as many lines as attributes are defined on your schema inside swagger_helper
This errors happens with wrong argument type. Just change "[FromBody]" to "[FromQuery]".
I faced similar issue; now, it's resolved.
You cannot pass parameter to HTTPGET thru Body.
To pass parameter to HTTPGet, there are 2 ways either use [FromRoute] or [FromQuery].
If u use [FromRoute], then
[HttpGet("{param1}/{param2}")]
public Person Get([FromRoute]string param1, string param2)
{
}
For PersonController,
from client side your url should be:
http://localhost:000/api/person/value1/value2
If u want to use [FromQuery]
[HttpGet]
public Person Get([FromQuery]string param1, string param2)
{
}
from client side your url should be:
http://localhost:000/api/person?param1=value1&param2=value2

How can I use SwaggerValidator(com.atlassian.oai.validator) to validate Lagom API Response?

Well, I am stuck in this scenario that I want to use Swagger API to validate response of my Lagom Service API.
Here is some sample code:
#Test
public void shouldPayloadFromFileConformToSchema() throws Exception {
// first test the plain json is valid with schema
SwaggerRequestResponseValidator validator = SwaggerRequestResponseValidator
.createFor("my-service-schema.yaml").build();
final Request validRequest = SimpleRequest.Builder.get("/myService/AL20170730/11111555556161/191919")
.withHeader("api-key", "TESTKEY")
.build();
Response validResponse = SimpleResponse.Builder.ok()
.withBody(ValidatorTestUtil.loadResponse("my_service_sample_response_2017_03_16")).build();
ValidationReport reportForText = validator.validate(validRequest, validResponse);
logger.info(
"shouldPayloadFromFileConformToSchema() ################# VALIDATION PAYLOAD REPORT ##################");
reportForText.getMessages().forEach((m) -> {
logger.info("{}", m);
});
assertFalse(reportForText.hasErrors());
logger.info(
"shouldPayloadFromFileConformToSchema() ################# VALIDATION PAYLOAD REPORT END ##################");
logger.info(validRequest.getHeaders().toString());
SwaggerModule swagger = new SwaggerModule();
}
When This code runs It seems that it goes into the service(as it prints some logs of the service.) but not invokes the method which will the database with the given values.
I need to do something here that it invokes the method of the service and validates the response on the basis of this swagger spec.
I saw this link but didn't get the solution How to validate API in tests with Swagger?
If you're looking to validate an actual interaction against your running service I'd recommend using the RestAssured module (https://bitbucket.org/atlassian/swagger-request-validator/src/master/swagger-request-validator-restassured/)
This will let you execute a request against your running service and then validate that the request/response interaction matches your swagger specification.
There's an example of its use in the examples module - https://bitbucket.org/atlassian/swagger-request-validator/src/master/swagger-request-validator-examples/src/test/java/com/atlassian/oai/validator/examples/restassured/SwaggerValidationFilterTestExample.java (note that the example there uses WireMock to stub out a real service, but you would replace that with your actual running service).

Spring #JsonView how can use request parameter or header

#JsonView
how can use like parameter from request:
#JsonView(header="range")
when response value,read request header range to exclude/include some field
JsonView provides "static" view mapping. so for your dynamic behaviour you can do like this:
// actual request handling is happened here
private ResponseEntity<SomeObject> processRequest();
// request handling with view limit in result
#JsonView(YourDefinedView.class)
#RequestMapping(value = "/request", headers={"range=include"})
public ResponseEntity<SomeObject> processWithView() {
return processRequest();
}
// request handling without view (no headers specified)
#RequestMapping(value = "/request")
public ResponseEntity<SomeObject> processWithoutView() {
return processRequest();
}
this will map your client to same request url, but depending on header it will provide view or not. Than you can create a set of methods, that will be using different #JsonView depending on headers information.
But with this you will limit only the data transfered to client, and the whole data load will happen on server. For example with database and JPA, if you would like not to fetch from database all that data you will end with javax.persistence.NamedEntityGraphs, which will change the general logic of your application - and will at the end of the day produce 2 different methods.
And if you would like to expose custom header with list of fields, to be serialized - custom DTO object, or Map<String, Object> (ugly-ugly-ugly) or custom HandlerMethodReturnValueHandler comes to your help.

Resources