diff --git a/README.md b/README.md index 3c28014..f829e8f 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,19 @@ An extremely __lightweight, flexible, and high performance__ [Undertow](http://u __NO MAGIC.__ -__LIMITED DEPENDENCIES.__ +Limited dependencies. -__< 400kb__ +< 400kb. JAX-RS compliant. -Verifiably [FAST](https://www.techempower.com/benchmarks/): [The latest benchmarks](https://www.techempower.com/benchmarks/) show Proteus outperforming 99% of other web frameworks. +Blazing fast!!! +[The latest Techempower benchmarks](https://www.techempower.com/benchmarks/) demonstrate Proteus outperforming 99% of all other web frameworks. +TL;DR +--------------- +Proteus rewrites your user friendly MVC controller methods into blazing fast Undertow response handlers at run time. +Easy on the developer and the metal. Getting Started --------------- @@ -57,10 +62,10 @@ Controller class methods respect standard Swagger / JAX-RS annotations: @GET @Path("/plaintext") @Produces((MediaType.TEXT_PLAIN)) -@ApiOperation(value = "Plaintext endpoint", httpMethod = "GET" ) -public void plaintext(HttpServerExchange exchange) +@ApiOperation(value = "Plaintext endpoint" ) +public ServerResponse plaintext(ServerRequest request) { - response("Hello, World!").contentType(PLAINTEXT_TYPE).send(exchange); + return response("Hello, World!").textPlain(); } ``` Proteus has three built in annotations: @@ -77,16 +82,43 @@ Proteus has three built in annotations: * ```io.sinistral.proteus.annotations.Chain``` * Wraps the endpoint handler in the provided array of ```io.undertow.server.HttpHandler``` classes. +Controller methods arguments support the following [JAX-RS annotations](https://docs.oracle.com/javaee/7/api/index.html?javax/ws/rs/PathParam.html): + +* @PathParam + * ```javax.ws.rs.PathParam``` + * Binds a url template parameter to the method parameter. + * i.e. if the path is `/dogs/{id}`, @PathParam("id") binds the path segment value to the method parameter. + +* @QueryParam + * ```javax.ws.rs.QueryParam``` + * Binds a HTTP query parameter to the method parameter. + +* @FormParam + * ```javax.ws.rs.FormParam``` + * Binds the form parameter within a request body to the method parameter. + +* @HeaderParam + * ```javax.ws.rs.HeaderParam``` + * Binds the value of a HTTP header to the method parameter. + +* @CookieParam + * ```javax.ws.rs.CookieParam``` + * Binds the value of a HTTP cookie to the method parameter. + + * @BeanParam + * ```javax.ws.rs.BeanParam``` + * Binds and attempts to convert the request body to an instance of the method parameter. + ## Return Types -##### Performance +#### Performance For total control and maximum performance the raw `HttpServerExchange` can be passed to the controller method. Methods that take an `HttpServerExchange` as an argument should __not__ return a value. In this case the method takes on __full responsibility__ for completing the exchange. -##### Convenience +#### Convenience The static method ```io.sinistral.proteus.server.ServerResponse.response``` helps create ```ServerResponse``` instances that are the preferred return type for controller methods. If the response object's `contentType` is not explicitly set, the `@Produces` annotation is used in combination with the `Accept` headers to determine the `Content-Type`. @@ -97,9 +129,9 @@ For methods that should return a `String` or `ByteBuffer` to the client users ca @Path("/hello-world") @Produces((MediaType.TEXT_PLAIN)) @ApiOperation(value = "Serve a plaintext message using a ServerResponse") -public ServerResponse plaintext(String message) +public ServerResponse plaintext(ServerRequest request, @QueryParam("message") String message) { - return ServerResponse.response("Hello, World!").contentType(PLAINTEXT_TYPE); + return ServerResponse.response("Hello, World!").textPlain(); } ``` By default, passing a `String` to the static `ServerResponse.response` helper function will convert it into a `ByteBuffer`. @@ -110,13 +142,14 @@ For other types of responses the following demonstrates the preferred style: @Path("/world") @Produces((MediaType.APPLICATION_JSON)) @ApiOperation(value = "Return a world JSON object", httpMethod = "GET", response=World.class ) -public io.sinistral.proteus.server.ServerResponse getWorld(Integer id, Integer randomNumber ) +public ServerResponse getWorld(ServerRequest request, @QueryParam("id") Integer id, @QueryParam("randomNumber") Integer randomNumber ) { - return io.sinistral.proteus.server.ServerResponse.response(new World(id,randomNumber)); + return response(new World(id,randomNumber)).applicationJson(); } ``` The entity can be set separately as well: > this disables static type checking! + ```java @GET @Path("/world") @@ -126,18 +159,22 @@ public io.sinistral.proteus.server.ServerResponse getWorld(Integer id, Integer { return io.sinistral.proteus.server.ServerResponse.response().entity(new World(id,randomNumber)); } + ``` `CompletableFuture>` can also be used as a response type: + ```java @GET @Path("/future/user") @ApiOperation(value = "Future user endpoint", httpMethod = "GET" ) -public CompletableFuture> futureUser() +public CompletableFuture> futureUser( ServerRequest request ) { return CompletableFuture.completedFuture(response( new User(123L) ).applicationJson() ); } ``` + In this case a handler will be generated with the following source code: + ```java public void handleRequest(final io.undertow.server.HttpServerExchange exchange) throws java.lang.Exception { CompletableFuture> response = examplesController.futureUser(); @@ -163,7 +200,7 @@ Optional parameters are also supported, here is a more complex endpoint demonstr @Path("/response/parameters/complex/{pathLong}") @ApiOperation(value = "Complex parameters", httpMethod = "GET") public ServerResponse> complexParameters( - final ServerRequest serverRequest, + ServerRequest serverRequest, @PathParam("pathLong") final Long pathLong, @QueryParam("optionalQueryString") Optional optionalQueryString, @QueryParam("optionalQueryLong") Optional optionalQueryLong, diff --git a/pom.xml b/pom.xml index a0e4bc6..0b84392 100644 --- a/pom.xml +++ b/pom.xml @@ -206,11 +206,18 @@ undertow-core ${version.undertow} - + + + + javax.ws.rs + javax.ws.rs-api + 2.1.1 + + com.squareup javapoet diff --git a/src/main/java/io/sinistral/proteus/server/swagger/AnnotationHelper.java b/src/main/java/io/sinistral/proteus/server/swagger/AnnotationHelper.java index a23116f..50d7990 100644 --- a/src/main/java/io/sinistral/proteus/server/swagger/AnnotationHelper.java +++ b/src/main/java/io/sinistral/proteus/server/swagger/AnnotationHelper.java @@ -28,6 +28,13 @@ public static FormParam createFormParam(Parameter parameter) @Override public String value() { + FormParam annotation = parameter.getAnnotation(FormParam.class); + + if(annotation != null) + { + return annotation.value(); + } + return parameter.getName(); } @@ -49,6 +56,13 @@ public static QueryParam createQueryParam(Parameter parameter) @Override public String value() { + QueryParam annotation = parameter.getAnnotation(QueryParam.class); + + if(annotation != null) + { + return annotation.value(); + } + return parameter.getName(); } @@ -69,6 +83,13 @@ public static PathParam createPathParam(Parameter parameter) @Override public String value() { + PathParam annotation = parameter.getAnnotation(PathParam.class); + + if(annotation != null) + { + return annotation.value(); + } + return parameter.getName(); } @@ -96,8 +117,27 @@ public Class annotationType() @Override public String name() { - return parameter.getName(); - } + QueryParam queryParam = parameter.getAnnotation(QueryParam.class); + FormParam formParam = parameter.getAnnotation(FormParam.class); + PathParam pathParam = parameter.getAnnotation(PathParam.class); + + if(queryParam != null) + { + return queryParam.value(); + } else if( pathParam != null ) + { + return pathParam.value(); + } + else if( formParam != null ) + { + return formParam.value(); + } + else + { + return parameter.getName(); + } + + } @Override public String value()