Comments (11)
Thanks Daniel,
You are getting a 403 on the POST because there isn't an HTTP method defined in the /contacts
resource in API Gateway. The ANY
method you have defined there in the {proxy+}
resource will only handle calls to sub-resources of contacts, for example /contacts/123
. To handle requests to the contacts resource your API should look like this:
/
|_ /contacts
|_ANY -> Lambda (POST/GET calls to /contacts)
|_/{proxy+}
|_ANY -> Lambda (calls to /contacts/123 - could be the same Lambda function for both
This is a start. I'll look into the code today but the 405 sounds like something with Jersey. I'll let you know what I find.
from aws-serverless-java-container.
Looking into it. Just to confirm, if you start this on your local machine using the embedded container in Jersey it responds correctly?
Could you also share a Swagger file with your API Gateway configuration (or a screenshot of the console?)
from aws-serverless-java-container.
I've tried to test something similar in local with Jersey 2.24. Looks like you cannot define a method with a sub-path in the interface, you have to define both in the implementation. Note that all of these tests are with the embedded Grizzly container in local. We'll get to Lambda and API Gateway next.
The DELETE method works for me when the interface is defined like this:
public interface ContactResource
{
@DELETE // you could skip this, it's here just for clarity
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response deleteContact(String id);
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response createContact(Map<String, String> co);
}
And the implementation looks like this
@Path("contacts")
public class ContactResourceImpl implements ContactResource {
@DELETE
@Path("{id}")
public Response deleteContact(@PathParam("id") String id)
{
return Response.status(204).build();
}
public Response createContact(Map<String, String> co)
{
return Response.status(200).build();
}
}
from aws-serverless-java-container.
So when I define in both Interface and Implementation, I get Duplicate definition errors. It thinks both the Interface and Implementation are definitions.
So I ran my JUnit tests, which I'm assuming fires up Grizzly. All tests worked. I've actually got more methods:
@HEAD
@Path("/ping")
@Consumes({MediaType.APPLICATION_JSON})
public Response ping(@QueryParam("numAdditionalInstancesToPing") int numAdditionalInstancesToPing);
@DELETE
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("/{proxy}")
public Response deleteContact(@PathParam("proxy") String id);
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response createContact(ContactObject co);
@PUT
@Path("/{proxy}")
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
public Response updateContact(@PathParam("proxy") String id, ContactObject co);
@GET
@Produces({MediaType.APPLICATION_JSON})
public Response getByCustId(@QueryParam("cid") String cid);
@GET
@Path("/{proxy}")
@Produces({MediaType.APPLICATION_JSON})
public Response getById(@PathParam("proxy") String id);
And it appears that all my tests work just fine.
Thanks,
David
from aws-serverless-java-container.
Is there a repo where we can take a look at the entire app? When I initialize my code with the @Path("{id})
only declared in the interface I get a 404 on the DELETE method, as if it couldn't find it.
On the 1st question, 403 is normally returned by API Gateway when you try to open a method that is not defined in the API. Would be good to see the API Gateway definition.
405 on the other hand, it's definitely not API Gateway unless you have explicitly defined that as a response code.
from aws-serverless-java-container.
Unfortunately, this is a private business app and I can't share all of the sources. I can share more of the code around this resource with a little editing.
Here's the API definition:
Complete (scrubbed) Resource interface:
package com.microstar.tap3.contact;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
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.microstar.tap3.contact.api.ContactObject;
public interface ContactResource
{
/*
* Returns nothing; just serves to keep the Lambda warm.
* <p>
* This method always returns immediately with an 200 OK status and no body.
*
* @return 200 OK status with no body.
*/
@HEAD
@Path("/ping")
@Consumes({MediaType.APPLICATION_JSON})
public Response ping(@QueryParam("numAdditionalInstancesToPing") int numAdditionalInstancesToPing);
@DELETE
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Path("/{proxy}")
public Response deleteContact(@PathParam("proxy") String id);
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public Response createContact(ContactObject co);
@PUT
@Path("/{proxy}")
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
public Response updateContact(@PathParam("proxy") String id, ContactObject co);
@GET
// @Path("/{cid}")
@Produces({MediaType.APPLICATION_JSON})
public Response getByCustId(@QueryParam("cid") String cid);
@GET
@Path("/{proxy}")
@Produces({MediaType.APPLICATION_JSON})
public Response getById(@PathParam("proxy") String id);
}
And complete (scrubbed) Resource impl:
import java.util.*;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Path("/contacts")
public class ContactResourceImpl implements ContactResource
{
private static final Logger LOG = LoggerFactory.getLogger(ContactResourceImpl.class);
public Response deleteContact(String id)
{
return deleteContact(id, null);
}
/* (non-Javadoc)
* @see com.microstar.tap3.contact.ContactResource#deleteContact(java.lang.String)
*/
public Response deleteContact(String id, String cid)
{
LOG.debug("Entering contacts::DELETE /" + id);
return Response.ok().build();
}
/* (non-Javadoc)
* @see com.microstar.tap3.contact.ContactResource#createContact(com.microstar.tap3.contact.api.ContactObject)
*/
public Response createContact(Map<String, String> co)
{
LOG.debug("Entering contacts::POST");
return Response
.status(Status.CREATED)
.entity(co)
.build();
}
/* (non-Javadoc)
* @see com.microstar.tap3.contact.ContactResource#updateContact(java.lang.String, com.microstar.tap3.contact.api.ContactObject)
*/
public Response updateContact(String id, Map<String, String> co)
{
LOG.debug("Entering contacts::PUT /" + id);
return Response
.ok()
.build();
}
/* (non-Javadoc)
* @see com.microstar.tap3.contact.ContactResource#getByCustId(java.lang.String)
*/
public Response getByCustId(String cid)
{
LOG.info("Entering contacts::GET ?cid=" + cid);
return Response.ok()
.entity(Collections.EMPTY_MAP)
.build();
}
/* (non-Javadoc)
* @see com.microstar.tap3.contact.ContactResource#getById(java.lang.String)
*/
public Response getById(String id)
{
LOG.info("Entering contacts::GET /" + id);
return Response
.ok()
.entity(Collections.EMPTY_MAP)
.build();
}
public Response ping(int numAdditionalInstancesToPing)
{
LOG.info("Entering ping::" + numAdditionalInstancesToPing);
LOG.info("ContactResourceImpl: " + this);
LOG.info("Thread: " + Thread.currentThread().getId());
return Response
.ok()
.build();
}
}
from aws-serverless-java-container.
Oh wow! I didn't know you had to do that! Thanks so much! That helps a lot. I'll try that out very shortly.
David
from aws-serverless-java-container.
I also edited your comment to add an even more scrubbed version of the impl class.
from aws-serverless-java-container.
Assuming the API Gateway configuration fixes your 403 on the POST. I'm now trying to replicate the 405 on the DELETE method without much luck.
When I start the server in local with your latest code, it responds to the DELETE method as expected. Same when I use Lambda and API Gateway.
This is the class I'm using to run the local tests:
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
public class Service {
public static void main(String[] args) throws IOException {
System.out.println("Starting Embedded Jersey HTTPServer...\n");
HttpServer httpServer = createHttpServer();
httpServer.start();
}
private static HttpServer createHttpServer() throws IOException {
ResourceConfig rc = new ResourceConfig()
.packages("com.sapessi.testapi") // this is the package where I created your Contact class
.register(JacksonFeature.class)
.register(LoggingFeature.class);
return GrizzlyHttpServerFactory.createHttpServer(getServerUri(), rc);
}
private static URI getServerUri() {
return UriBuilder.fromUri("http://" + getLocalHostname() + "/").port(8085).build();
}
private static String getLocalHostname() {
String hostName = "localhost";
try {
hostName = InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return hostName;
}
}
And this is my simple Lambda handler:
import com.amazonaws.serverless.proxy.internal.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
private ResourceConfig jerseyApplication = new ResourceConfig()
.packages("com.sapessi.testapi")
.register(JacksonFeature.class);
private JerseyLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler
= JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);
public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
return handler.proxy(awsProxyRequest, context);
}
}
from aws-serverless-java-container.
So adding the additional method under the /contacts resource made everything work!
Not sure I totally understand why, but as long as it works, I'm happy.
I'd also be interested in contributing to this project if you are looking for developers/maintainers. Let me know.
Thanks,
David
from aws-serverless-java-container.
Thanks David, I'm currently working on issues #15 and #16. However, if there are other features you need in the library, feel free to open a new issue and send a pull request. We are always looking for feedback on the library.
I'm going to close this issue for now. thanks for all the help to debug this.
from aws-serverless-java-container.
Related Issues (20)
- Not able to apply spring security on pet-store-native sample HOT 6
- No public method named handleRequest with appropriate method signature on SpringBootLambdaContainerHandler HOT 12
- `IAM` authorizer is discarded HOT 2
- Not getting reponse back directly from filter HOT 1
- Reinitialization of Spring context when using SnapStart HOT 3
- Spring Cloud Gateway throwing "hostname can't be null" based on my request only having headers and not multi-value headers HOT 4
- ServletContext: Servlet.getServletConfig() is null HOT 1
- Cannot invoke "org.springframework.web.servlet.DispatcherServlet.getServletContext()" because "servlet" is null HOT 4
- org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: No content to map due to end-of-input HOT 1
- org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: No content to map due to end-of-input HOT 8
- Cannot deserialize value of type `java.lang.String` from Object value HOT 3
- SpringDelegatingLambdaContainerHandler unable to respond with rendered templates HOT 10
- SpringDelegatingLambdaContainerHandler does not handle Headers correctly for FunctionUrls HOT 7
- Cookies not able to be deleted by setting max-age to 0 HOT 1
- com.amazonaws.serverless.exceptions.InvalidRequestEventException: The incoming event is not a valid request from Amazon API Gateway or an Application Load Balancer HOT 1
- 2.x release HOT 1
- AwsProxyResponse class is generating response JSON which is rejected by API gateway with Malformed error HOT 3
- Moved AwsProxyRequestBuilder into test package HOT 4
- How to handle spring security with springboot serverless HOT 2
- Null Pointer While Setting Filter Post Startup Lambda Handler HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from aws-serverless-java-container.