Code Monkey home page Code Monkey logo

rest.vertx's People

Contributors

bojanlet avatar dependabot[bot] avatar drejc avatar smil2k avatar tkirsch avatar younesrahimi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rest.vertx's Issues

Getting error when using validation (hibernate) and method with no parameters

Hi! Thanks for that awesome library!

When writing Controller methods with no parameters I get:
Error java.lang.IllegalArgumentException: HV000116: The method parameter array cannot not be null

package com.infloop.auth.controllers;

import com.zandero.rest.annotation.ResponseWriter;
import com.zandero.rest.writer.FileResponseWriter;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;


@Path("/")
@Api(value = "/", description = "Swagger")
@Produces({MediaType.APPLICATION_JSON})
public class SwaggerController {

    @GET
    @Path("/swagger/swagger.json")
    @ApiOperation(value = "Get swagger documentation",
            notes = "Returns a swagger.json"
    )
    @ResponseWriter(FileResponseWriter.class)
    public String get() {  // <<== here
        return "swagger.json";
    }
}

Workaround for this:

package com.infloop.auth.controllers;

import com.zandero.rest.annotation.ResponseWriter;
import com.zandero.rest.writer.FileResponseWriter;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;


@Path("/")
@Api(value = "/", description = "Swagger")
@Produces({MediaType.APPLICATION_JSON})
public class SwaggerController {

    @GET
    @Path("/swagger/swagger.json")
    @ApiOperation(value = "Get swagger documentation",
            notes = "Returns a swagger.json"
    )
    @ResponseWriter(FileResponseWriter.class)
    public String get(final @DefaultValue("") String empty) {   // <<== here
        return "swagger.json";
    }
}

Possible solution:
when ArgumentProvider.getArguments returns null - do not validate parameters.

in RestRouter.java

...
try {
  Object[] args = ArgumentProvider.getArguments(method, definition, context, readers, providers, injectionProvider);
  validate(method, definition, validator, toInvoke, args);
  fut.complete(method.invoke(toInvoke, args));
}
...

I'm using the following dependencies:

        compile group: 'com.zandero', name: 'rest.vertx', version: '0.8.6'
        compile group: 'io.swagger', name: 'swagger-jaxrs', version: '1.5.21'

        compile group: 'io.swagger', name: 'swagger-core', version: '1.5.21'
        compile group: 'io.swagger', name: 'swagger-parser', version: '1.0.28'

        compile group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'

        compile group: 'javax.el', name: 'javax.el-api', version: '3.0.0'
        compile group: 'org.glassfish.web', name: 'javax.el', version: '2.2.4'
        compile group: 'org.hibernate', name: 'hibernate-validator', version: '6.0.13.Final'
        compile group: 'org.hibernate', name: 'hibernate-validator-annotation-processor', version: '6.0.13.Final'

RestRouter does take subRoutes into account when using regex path's

I am using RestRouter mounted as a subRouter like this:

    final Router restRouter = createRestRoutes(vertx);
    mainRouter.mountSubRouter("/prod/rest", restRouter);

But when one of the rest paths are using regular expressions, the argument number is not taking this into account, which results in invalid data being parsed.

When using Proxies, RouteDefinitions are not created correctly

I am working on adding Spring Validation to the REST Controllers so I have created an interface

@Path("/user")
public interface LoginControllerContract {
    @Path("/login")
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    Future<UserInfo> login(@Valid Credentials credentials);
}

And an implementation class

@Component
@Slf4j
@Validated
public class LoginController implements LoginControllerContract {
    private final JDBCClient jdbcClient;

    @Autowired
    public LoginController(JDBCClient jdbcClient) {
        this.jdbcClient = jdbcClient;
    }

    @Override
    public Future<UserInfo> login(@Valid Credentials credentials) {

In my bootstrap verticle where I declare an @Autowired to LoginControllerContract, I register the Controller with rest.vertx

    @Autowired
    private LoginControllerContract loginController;
    /**
     * Sets up the router with the login controller
     * @param router the vertx web router to set up
     */
    private void setupLoginController(Router router) {
        new RestBuilder(router)
            .register(loginController)
            .errorHandler(HttpStatusExceptionHandler.class)
            .build();
    }

When I disable the validation AOP, I get an actual object to LoginController and the registration works fine and I can reach the endpoint /user/login. However when I enable the validation AOP, Spring proxies the LoginController class which makes the registration not do anything and I get a "Resource Not Found" error. I tried following the code but it seems the method AnnotationProcessor::isRestCompatible is not correctly identifying the methods which are rest compatible.

	/**
	 * @param method to check if REST compatible
	 * @return true if REST method, false otherwise
	 */
	private static boolean isRestCompatible(Method method) {

		return (!method.getDeclaringClass().isInstance(Object.class) &&
		        !isNative(method) && !isFinal(method) &&
		        (isPublic(method) || isInterface(method) || isAbstract(method)));
	}

Within the proxy is choosing all the methods which makes the RouteDefinition itself to not be built correctly.

RestBuilder::getRouter passes a List<Object> as an array element, instead of converting the list to an array of objects

	private List<Object> apis = new ArrayList<>();
	private Router getRouter() {
		if (vertx == null) {
			return RestRouter.register(router, apis);
		}

		return RestRouter.register(vertx, apis);
	}
	public static Router register(Vertx vertx, Object... restApi) {

		Assert.notNull(vertx, "Missing vertx!");

		Router router = Router.router(vertx);
		return register(router, restApi);
	}

calling

        return new RestBuilder(vertx)
            .register(new MyObject())
            .errorHandler(WebApplicationExceptionHandler.class)
            .build();
@Path("/the")
@Slf4j
public class MyObject {
    @Path("/path")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public void login() {

From Intellij debugger

restApi:java.lang.Object[1] = {java.lang.Object[1]@5307}
->0: {java.util.ArrayList@5248} size = 1
-->0: {MyObject@2245}

which leads to an empty Map<RouteDefinition, Method> definitions in

	public static Router register(Router router, Object... restApi) {

		// TODO: split into smaller chucks

		Assert.notNull(router, "Missing vertx router!");
		Assert.isTrue(restApi != null && restApi.length > 0, "Missing REST API class object!");
		assert restApi != null;

		for (Object api : restApi) {

			// check if api is an instance of a class or a class type
			if (api instanceof Class) {

				Class inspectApi = (Class) api;

				try {
					api = ClassFactory.newInstanceOf(inspectApi, injectionProvider, null);
				}
				catch (ClassFactoryException | ContextException e) {
					throw new IllegalArgumentException(e.getMessage());
				}
			}

			Map<RouteDefinition, Method> definitions = AnnotationProcessor.get(api.getClass());

			boolean bodyHandlerRegistered = false;
			boolean cookieHandlerRegistered = false;

			for (RouteDefinition definition : definitions.keySet()) {

because Object api here is not really MyObject but java.util.ArrayList which is not annotated

Allow asynchronous web services

For the moment, all the services I wrote had the "Blocking" annotation.

It works pretty well, but its is not an ideal solution for an asynchronous framework like vertx.

It would be interesting to have a way of writing asynchronous methods, either with the Jax-RS 2.0 AsyncResponse (which is quite complex) or by letting the methods have a way of calling a "resume" method with the result as a parameter.

rest.vertx is great, but i want to kone how to implement AsyncResult in restMethod

i want implement a async controller method like vertx.web

here is my code

@GET
    @Path("/create")
    @Produces(MediaType.APPLICATION_JSON)
    @ResponseWriter(VertxResponseWriter.class)
    public Handler<Void> create(@Context RoutingContext context,
                                @Context HttpServerResponse response,
                                @Context Vertx vertx) {

        System.out.println(Thread.currentThread().getName() + "create start");
        context.request().getParam("id");
        SpaceDO spaceDO = new SpaceDO();
        Handler<Void> result = res -> {
            System.out.println("really return");
            response.setStatusCode(200)
                    .putHeader("content-type", "application/json")
                    .end("vapsce return");

        };
        spaceService.create(spaceDO, dbResult -> {
            System.out.println(Thread.currentThread().getName() + "async complete");
            result.handle(dbResult.result());
        });
        System.out.println(Thread.currentThread().getName() + "create method end");
        return result;
    }

but the com.zandero.rest.RestRouter#produceResponse method end end response mandatory,and system invoke error: java.lang.IllegalStateException: Response has already been written

if (!response.ended()) {
			response.end();
		}

why do this, and could i comment that statement in some fork?

java.lang.IllegalStateException: Response has already been written

    TestRest rest = new TestRest();
    Router router = RestRouter.register(vertx, rest);
    TemplateEngine engine = ThymeleafTemplateEngine.create();
    TemplateHandler templateHandler = TemplateHandler.create(engine);
    router.route().handler(templateHandler);

TestRest .java

    @GET
    @Path("/echo1")
    @Produces(MediaType.TEXT_HTML)
    public void echo1(@Context RoutingContext routingContext, @Context Vertx vertx) {
        routingContext.put("text","welcome!").reroute("index.html");
    }

Html display right, but console have exception :

java.lang.IllegalStateException: Response has already been written
	at io.vertx.core.http.impl.HttpServerResponseImpl.checkWritten(HttpServerResponseImpl.java:572)
	at io.vertx.core.http.impl.HttpServerResponseImpl.end0(HttpServerResponseImpl.java:407)
	at io.vertx.core.http.impl.HttpServerResponseImpl.end(HttpServerResponseImpl.java:325)
	at io.vertx.core.http.impl.HttpServerResponseImpl.end(HttpServerResponseImpl.java:314)
	at com.zandero.rest.exception.GenericExceptionHandler.write(GenericExceptionHandler.java:14)
	at com.zandero.rest.exception.GenericExceptionHandler.write(GenericExceptionHandler.java:9)
	at com.zandero.rest.RestRouter.handleException(RestRouter.java:428)
	at com.zandero.rest.RestRouter.lambda$getHandler$11(RestRouter.java:375)
	at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:223)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:101)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:120)
	at io.vertx.ext.web.impl.RouterImpl.accept(RouterImpl.java:79)
	at io.vertx.core.http.impl.ServerConnection.processMessage(ServerConnection.java:444)
	at io.vertx.core.http.impl.ServerConnection.handleMessage(ServerConnection.java:156)
	at io.vertx.core.http.impl.HttpServerImpl$ServerHandlerWithWebSockets.handleMessage(HttpServerImpl.java:661)
	at io.vertx.core.http.impl.HttpServerImpl$ServerHandlerWithWebSockets.handleMessage(HttpServerImpl.java:614)
	at io.vertx.core.net.impl.VertxHandler.lambda$channelRead$1(VertxHandler.java:150)
	at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:342)
	at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:200)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:148)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.vertx.core.http.impl.HttpServerImpl$Http2UpgradeHandler.channelRead(HttpServerImpl.java:929)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:310)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:284)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.end(Http1xOrH2CHandler.java:49)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.channelRead(Http1xOrH2CHandler.java:27)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1359)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:935)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:645)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:580)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:497)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:459)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
	at java.lang.Thread.run(Thread.java:745)

Allows to use @QueryParam with classes that have a String constructor (JAX-RS compatibility)

according to JAX-RS doc, the type of a query parameter can:

  • Be a primitive type
  • Have a constructor that accepts a single String argument
  • Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String))
  • Have a registered implementation of ParamConverterProvider JAX-RS extension SPI that returns a ParamConverter instance capable of a "from string" conversion for the type.
  • Be List, Set or SortedSet, where T satisfies 2, 3 or 4 above. The resulting collection is read-only.

ok, that's a lot to implement... maybe the 2 first one would be nice :-)

Note : I tried to use a RequestReader, but it does'nt seem to work with QueryParam. For the moment I'll stick with string parameters

Not found handler order

Option to place not found handler on specific point before next handler is invoked.

For instance:

not found /api/* handler placed after all other /api/ handler but before /* handler.

Guice injection is not working

I follow the instruction in "Injection" section, but got exception, the same exception happened in your example project.

java.lang.IllegalArgumentException: Failed to instantiate class of type: com.sc.application.rest.TestRest3, class needs empty constructor!

package com.sc.application.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.google.inject.Guice;
import com.google.inject.Inject;

import io.vertx.core.json.JsonObject;

@Path("/test3")
public class TestRest3 {

	
	private  MyService service;

	
//	public TestRest3() {
//
//		service = Guice
//	              .createInjector(new GuiceInjectionProvider())
//	              .getInstance(MyService.class);
//	}
	
	@Inject
	public TestRest3(MyService someService) {

		service = someService;
	}


	@GET
	@Path("/test")
	@Produces(MediaType.APPLICATION_JSON)
	public JsonObject echo() {

		JsonObject message = new JsonObject();
		message.put("collection", "mycollection").put("document", new JsonObject().put("name", this.service.call()));


		return message;
	}
}


package com.sc.application.rest;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.zandero.rest.injection.InjectionProvider;

public class GuiceInjectionProvider extends AbstractModule implements InjectionProvider  {

	private Injector injector;

	public GuiceInjectionProvider() {
		injector = Guice.createInjector(this);
	}

	@Override
	protected void configure() {
		bind(MyService.class).to(MyServiceImpl.class);
	}

	@Override
	public Object getInstance(Class clazz) {
		return injector.getInstance(clazz);
	}
}



package com.sc.application.rest;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;

import java.util.HashMap;
import java.util.Map;

import com.sc.application.service.SomeDatabaseService;
import com.zandero.rest.RestBuilder;
import com.zandero.rest.RestRouter;


public class App extends AbstractVerticle {


	@Override
	public void start() {
		
		Router router = new RestBuilder(vertx)
                .injectWith(GuiceInjectionProvider.class)
                .register(TestRest3.class)
                .build();
		vertx.createHttpServer().requestHandler(router::accept).listen(8080);
	}

	
}

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.sc</groupId>
	<artifactId>sc-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>sc-service</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<vertx.version>3.5.2</vertx.version>
	</properties>

	<dependencies>
		<!-- https://mvnrepository.com/artifact/junit/junit -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>


		<dependency>
			<groupId>io.vertx</groupId>
			<artifactId>vertx-core</artifactId>
			<version>${vertx.version}</version>
		</dependency>

		<dependency>
			<groupId>io.vertx</groupId>
			<artifactId>vertx-sync</artifactId>
			<version>${vertx.version}</version>
		</dependency>

		<dependency>
			<groupId>io.vertx</groupId>
			<artifactId>vertx-service-proxy</artifactId>
			<version>${vertx.version}</version>
		</dependency>

		<dependency>
			<groupId>io.vertx</groupId>
			<artifactId>vertx-service-proxy</artifactId>
			<version>${vertx.version}</version>
			<classifier>processor</classifier>
		</dependency>

		<dependency>
			<groupId>io.vertx</groupId>
			<artifactId>vertx-service-proxy</artifactId>
			<version>${vertx.version}</version>
		</dependency>

		<dependency>
			<groupId>io.vertx</groupId>
			<artifactId>vertx-docgen</artifactId>
			<version>3.5.1</version>
		</dependency>

		<dependency>
			<groupId>io.vertx</groupId>
			<artifactId>vertx-codegen</artifactId>
			<version>${vertx.version}</version>
			<scope>provided</scope>
		</dependency>

<!-- 		<dependency> -->
<!-- 		    <groupId>io.advantageous.qbit</groupId> -->
<!-- 		    <artifactId>qbit-core</artifactId> -->
<!-- 		    <version>2.0.0</version> -->
<!-- 		</dependency> -->
		
<!-- 		<dependency> -->
<!-- 		    <groupId>io.advantageous.qbit</groupId> -->
<!-- 		    <artifactId>qbit-vertx</artifactId> -->
<!-- 		    <version>2.0.0</version> -->
<!-- 		</dependency> -->
		
<!-- 		<dependency> -->
<!-- 		    <groupId>io.advantageous.qbit</groupId> -->
<!-- 		    <artifactId>qbit-admin</artifactId> -->
<!-- 		    <version>2.0.0</version> -->
<!-- 		</dependency> -->
		
<!-- 		<dependency> -->
<!-- 		    <groupId>io.advantageous.qbit</groupId> -->
<!-- 		    <artifactId>qbit-service-discovery</artifactId> -->
<!-- 		    <version>2.0.0</version> -->
<!-- 		</dependency> -->
		
		
<!-- 		<dependency> -->
<!-- 		    <groupId>io.advantageous.qbit</groupId> -->
<!-- 		    <artifactId>qbit-guice</artifactId> -->
<!-- 		    <version>1.1.0.RELEASE</version> -->
<!-- 		</dependency> -->
		
		
		

		
		<!-- https://mvnrepository.com/artifact/com.google.inject/guice -->
		<dependency>
		    <groupId>com.google.inject</groupId>
		    <artifactId>guice</artifactId>
		    <version>4.2.0</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
		<dependency>
		    <groupId>com.google.guava</groupId>
		    <artifactId>guava</artifactId>
		    <version>25.1-jre</version>
		</dependency>
		
		
		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
		<dependency>
		    <groupId>org.apache.httpcomponents</groupId>
		    <artifactId>httpcore</artifactId>
		    <version>4.4.9</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/com.zandero/rest.vertx -->
		<dependency>
		    <groupId>com.zandero</groupId>
		    <artifactId>rest.vertx</artifactId>
		    <version>0.8.4</version>
		</dependency>
		
		
		
	</dependencies>

	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.1</version>
					<configuration>
						<source>1.8</source>
						<target>1.8</target>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>

Starting App get exception

SEVERE: Failed in deploying verticle
java.lang.IllegalArgumentException: Failed to instantiate class of type: com.sc.application.rest.TestRest3, class needs empty constructor!
at com.zandero.rest.RestRouter.register(RestRouter.java:118)
at com.zandero.rest.RestRouter.register(RestRouter.java:80)
at com.zandero.rest.RestBuilder.getRouter(RestBuilder.java:328)
at com.zandero.rest.RestBuilder.build(RestBuilder.java:360)
at com.sc.application.rest.App.start(App.java:70)
at io.vertx.core.AbstractVerticle.start(AbstractVerticle.java:106)
at io.vertx.core.impl.DeploymentManager.lambda$doDeploy$8(DeploymentManager.java:483)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:339)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)

Not able to register Custom Filter Class in Vertx RestRouter

I have created custom Authentication Filter:
public class AuthenticationFilter implements javax.ws.rs.container.ContainerRequestFilter

As this filter should invoked for each rest API call, I am trying to register it in router using:
RestRouter.register(vertx, AuthenticationFilter.class)

but this filter is not invoked.

in case of other J2EE Container it is working fine as it uses:
register of ResourceConfig .

Please suggest any Solution or alternative for this Issue.

Allow methods without @Path annotation (JAX-RS compatibilty)

in JAX-RS, methods annotated with @GET, @POST, @PUT, or @DELETE don't need a @PATH annotation if class already has one (they will get the path from the class).

For the moment, such methods are not added to the router by RestRoute.register, you need to add a @Path("/").

SEVERE: Failed to instantiate class of type: com.zandero.rest.example.rest.EchoRest, class needs empty constructor!

I downloaded rest.vertx.example and run it, exception throw when I access /echo
If I create a new empty constructor, guice could not inject MyService into the rest

SEVERE: Failed to instantiate class of type: com.zandero.rest.example.rest.EchoRest, class needs empty constructor!
java.lang.IllegalArgumentException: Failed to instantiate class of type: com.zandero.rest.example.rest.EchoRest, class needs empty constructor!
at com.zandero.rest.RestRouter.register(RestRouter.java:118)
at com.zandero.rest.RestRouter.register(RestRouter.java:80)
at com.zandero.rest.RestBuilder.getRouter(RestBuilder.java:328)
at com.zandero.rest.RestBuilder.build(RestBuilder.java:360)
at com.zandero.rest.example.ServerVerticle.start(ServerVerticle.java:58)
at io.vertx.core.AbstractVerticle.start(AbstractVerticle.java:111)
at io.vertx.core.impl.DeploymentManager.lambda$doDeploy$8(DeploymentManager.java:434)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:337)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:403)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:445)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
at java.lang.Thread.run(Thread.java:748)

DELETE body with empty body not working

Good evening,

I've seen the issue has been closed, but i'm seeing some odd behavior.

Using vertx web 3.8.0, updated rest.vertx to 0.8.9.

I'm having one path with a GET and a DELETE
1:
@get
@path("tokens/:tokenReferenceId")
2:
@delete
@path("tokens/:tokenReferenceId")

I'm noticing after the update the DELETE annotated method is results in a 404. But after adding an empty body to my delete request, the DELETE annotated method is found again.

It looks like DELETE now requires a body to be able to be identified.

For now for me it's not a problem because my calls are made generic in my front-end, so adding an empty object is not that big of a deal. But it was unexpected.

You want me to open a new report?

Originally posted by @jsirach in #42 (comment)

Support Multiple HTTP Methods/Verbs for Same Route

Currently one can set only one HTTP method for a route. While in other frameworks like Spring Web you can set multiple HTTP methods for the same route. Being able to set multiple HTTP methods lets the developer migrate a route's HTTP method over time from one to another or simply to respond to the same requests with different HTTP methods.

Example code:

@Path("/test")
public class TestResource{

    @GET
    @POST
    public String getName(){
        return "Hello Java";
    }
}

Make rest.vertx more extensible

At first thank you for your project. I really like it because it is a good alternative to the official Vert.x Web Contract library and enables me to start a project rapidly.

I use rest.vertx with Kotlin and would like to have support for Kotlin coroutines. At the moment it is not possible to change some behaviours of rest.vertx without forking your project. I suggest to change the structure a bit so that support for Kotlin coroutines or Quasar fibers will be possible as an optional external library.

I would like to support something like this (this will not work at the moment):

@POST
@Path("/echo")
suspend fun echoFunction(echo: Echo): Echo {
    // ... do something in an asynchronous way but in synchronous coroutine style ...
    return echo
}

But at the moment I can only do this (with some code around the coroutine):

@POST
@Path("/echo")
fun echoFunction(echo: Echo): Future<Echo> {
    Future future = Future.future()

    // launch coroutine
    GlobalScope.launch(vertx.dispatcher) {
        // ... do something in an asynchronous way but in synchronous coroutine style ...
        future.complete(echo)
    }

    return future
}

The first example is much more concise. In order to enable it from external code I need to open rest.vertx a bit:

  • The AnnotationProcessor methods would be changed from static to instance methods. A few methods would be changed from private to protected.
  • A RouteHandlerFactory would be introduced that creates Handler for each operation.

Both would be members of RestRouter. The RestRouter would have a possibilty to overwrite the AnnotationProcessor by something like a sub class and a new RouterHandlerFactory can be added that would for example support Kotlin coroutines or Quasar fibers.

What do you think about such a change? I think rest.vertx would be even better if we made it a bit more extensible.

DELETE endpoints cannot have a body

The HTTP method "DELETE" is allowed to have a body. But rest.vertx does not allow.

Exception in thread "vert.x-eventloop-thread-0" java.lang.IllegalArgumentException: one.two.three.MyController.delete(Payload arg0) - Missing argument annotation (@PathParam, @QueryParam, @FormParam, @HeaderParam, @CookieParam or @Context) for: arg0!
	at com.zandero.utils.Assert.isFalse(Assert.java:38)
	at com.zandero.utils.Assert.isTrue(Assert.java:51)
	at com.zandero.rest.AnnotationProcessor.get(AnnotationProcessor.java:65)
	at com.zandero.rest.RestRouter.register(RestRouter.java:124)
	at com.zandero.rest.RestBuilder.getRouter(RestBuilder.java:406)
	at com.zandero.rest.RestBuilder.build(RestBuilder.java:451)

Not possible to have multiple mediatype's in @Produces for a method

When a method is annotated with

@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })

And the request is sent with the "Accept: application/xml", the response is returned as JSON.

If it is called with "Accept: application/json", the response works ok and returns json.

It looks like this code is the main problem:

			for (MediaType produces : definition.getProduces()) {
				response.putHeader(HttpHeaders.CONTENT_TYPE, produces.toString());
			}

By default ALL elements in Produces are added. This means the last will always win. Instead it should look at what is in Accept header on the request.

Exception when validating a method taking no parameter.

Hi again,

After I registered a Validator I started to get the following exception when the RestRouter tries to validate a function that takes no parameter. The validator library that I am using is hibernate-validators 6.0.13.

The exception I get is the following:

java.lang.IllegalArgumentException: HV000116: The method parameter array cannot not be null.
	at org.hibernate.validator.internal.util.Contracts.assertNotNull(Contracts.java:45) ~[hibernate-validator-6.0.13.Final.jar:6.0.13.Final]
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:222) ~[hibernate-validator-6.0.13.Final.jar:6.0.13.Final]
	at com.zandero.rest.RestRouter.validate(RestRouter.java:533) [rest.vertx-0.8.7.jar:?]

And below you will find the signature of one of the method that cause the problem:

@GET
@RolesAllowed(Scope.CATEGORIES_LIST_PUBLIC)
fun listCategories(): Future<List<CategoryUserResource>>

A simple fix may be to check if there is at least one parameters before trying to call validateParameters.

Best,

Detection of regex in path is too sensible

Hi,

We are using in our application versioned path like the following:

@Path("/api/admin/1.0/categories")

Unfortunately, the ValidatingUtils.isRegEx is picking 1.0 as a valid regex and is creating a path param named "param0" for it. Therefore we need to add a parameter @PathParam("param0") in all of our methods otherwise we get this error:

Invalid parameter type for: @PathParam("param0") for: /api/admin/1.0/categories

We have found multiple ways to overcome the issue but it could be good if in the future we can enable/disable which type of detection we want for path params.

Best,

How to disable logging caught exception as an Error

One of my functions inside a Controller throws an Exception, and it is already caught by an exception handler (defined by RestRouter.getExceptionHandlers().register()), but it still shows up as an error

[vert.x-eventloop-thread-0] ERROR RestRouter - Handling exception: 
java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.zandero.rest.RestRouter.lambda$null$2(RestRouter.java:472)
	at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$2(ContextImpl.java:272)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:834)

doc spelling

the front page doc uses the word vertical - when it should be verticle. Minor - but good to fix.

Unable to write application/json if a JaxResponse is used

When i have a method returning a javax.ws.rs.core.Response, it is not possible to have the data returned as Json, due to the condition below in JaxResponseWriter class. Is there any reason for this?

			if (writer != null && !(writer instanceof JsonResponseWriter)) {
				writer.write(jax.getEntity(), request, response);
			}
			else {
				response.end(jax.getEntity().toString());
			}

@Produces({MediaType.APPLICATION_XML}) error

    @GET
    @Path("/h1")
    @Produces({MediaType.APPLICATION_XML})
    public Response h1(){
        User u = new User();
        u.setPassword("p");
        u.setName("a");
        return Response.ok(u).build();
    }

This page contains the following errors:
error on line 1 at column 1: Document is empty
Below is a rendering of the page up to the first error.

Why this happen?

[Feature request] Input data validation

Hi!
I think it would be terribly cool to add support for javax.validation and @Valid parameter annotation, which would automatically check for violations in deserialized entities and throw ConstraintViolationException in case of found violations.

Errors are rendered as plain text even when requesting JSON

When there are exceptions raised by the body handler (for example JSON description not conform to the body type), they are rendered as plain text, not JSON, although the content-type is application/json as expected

it would be interesting to have a way to customize the output for exceptions.

(tested on 0.2 - Great project by the way!)

JAX-RS annotation inheritance

As I use Enunciate for documentation, the web services classes are divided in interface and implementation. In that case, according to JAX-RS specs, I should'nt repeat the annotations on implementation. But rest.vertx doesn't find the method in that case, as it use only getAnnotation() on the method without climbing to the superclass. I assume the same happens for parameter annotations.

see https://stackoverflow.com/questions/25916796/inheritance-with-jax-rs for the link to the spec, and https://stackoverflow.com/questions/10082619/how-do-java-method-annotations-work-in-conjunction-with-method-overriding/10082663 for an example of climbing to the superclass to read method annotations.

Can't get session problem

        TestRest rest = new TestRest();
        Router router = RestRouter.register(vertx, rest);
        router.route().handler(CookieHandler.create());
        SessionHandler handler = SessionHandler.create(LocalSessionStore.create(vertx));
        router.route().handler(handler);

TestRest.java

    @GET
    @Path("/echo")
    @Produces(MediaType.TEXT_HTML)
    public String echo(@Context RoutingContext routingContext) {
        Session session = routingContext.session();
        return "Hello world!";
    }

Session session = routingContext.session();
session == null , something wrong?

Given exception handler not used

If i'm overlooking something, please correct me.

Currently i'm building a router with

for (Collection api : Collection.values()) { apiList.add(api.getResource()); } return new RestBuilder(vertx) .register(apiList.toArray()) .errorHandler(RestFallbackExceptionHandler.class) .build();

Which correctly builds the router.

I have created an error handler with:

`
@produces("application/json")
public class RestFallbackExceptionHandler implements ExceptionHandler {

/**
 * Logger for the handler.
 */
Logger LOG = LogManager.getLogger(RestFallbackExceptionHandler.class);

/**
 * Writes the error to the output.
 *
 * @param exception The exception to get the data from.
 * @param request The http request made.
 * @param response The response to be send back to the user.
 */
@Override
public void write(DefaultRestException exception, HttpServerRequest request, HttpServerResponse response) {
    response.setStatusCode(exception.getStatus());
    try {
        response.end(Serialization.getDefaultObjectMapper().writeValueAsString(exception));
    } catch (JsonProcessingException ex) {
        LOG.error("Could not serialize default REST error exception");
        response.end(exception.getMessage());
    }
}

}
`

Following the example on the github page.

When i access a REST endpoint for which the user is unauthorized i'm getting the following result:

"HTTP 401 Unauthorized"

With in my log file:
"Resolving to generic exception handler: com.zandero.rest.exception.GenericExceptionHandler"

I was expecting my exception handler "RestFallbackExceptionHandler" to be used.

The Serialization.getDefaultObjectMapper() is a jackson objectmapper

Add a way to set common logic in rest handlers

In my application, web service are multi-tenant, so they all look the same

@POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Blocking @Path("/")
public ServiceResult dosomething(myClass param, @Context User user) {
	ServiceResult res = new ServiceResult();
	doAsTenant(user, () -> {
			... some logic here
	});
	return res;
}

doAsTenant sets a thread local with the tenant id to protect access to resources.

Do you think it would be possible to add a common logic directly in the rest handlers, in order to keep only the business logic in the web service class ? For example, have a pre and post step for each service, set in RestRouter options ?

Response can not end async method result

Thank you for reply and close the last issue I have! There is something like this in Vertx-Web I can not do with rest.vertx:

In Vertx-Web I call async method like below and it is not blocking io, it doesn't need to run under worker thread:

public void test(Vertx vertx) {
		Router.router(vertx).post("/product/save").handler(ctx -> {
	        Product product = new Product();
			CompletableFuture<Product> result = productService.saveProduct(product);
			result.whenComplete((p, e) -> vertx.runOnContext(none -> {
	            ctx.response().end(Json.encodePrettily(p));
	        }));

	    });
	}

And this above code work fine, async and not blocking any thread.

In rest.vertx, I do this:

@POST
	@Path("/save")
	@Consumes(MediaType.APPLICATION_JSON)
	public HttpServerResponse save(@Context HttpServerResponse response, Product product) throws InterruptedException, ExecutionException {
		CompletableFuture<Product> result = productService.saveProduct(product);
		result.whenComplete((p, e) -> {
			System.out.println("Product: " + p);
			response.putHeader("content-type", "application/json; charset=utf-8")
		      .end(Json.encodePrettily(p));
		});
		
		return response;
		
	}

If I put break point in return response, when I hit /product/save, it return me the json result. However if I release break point, empty result produced.

I also try return void, but it return empty:

@POST
	@Path("/save")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public void save(@Context HttpServerResponse response, Product product) throws InterruptedException, ExecutionException {
		CompletableFuture<Product> result = productService.saveProduct(product);
		result.whenComplete((p, e) -> {
			System.out.println("Product: " + p);
			response.putHeader("content-type", "application/json; charset=utf-8")
		      .end(Json.encodePrettily(p));
		});
		
	}

The problem now is how to have response.end( result) in whenComplete() of Future, but still produce the result like Vertx-Web.
Thanks!

Routes checking @RolesAllowed have order 0

Hi,

I was having the following exception on my POST endpoints with a @RoleAllowed annotation:

java.lang.IllegalStateException: Request has already been read
	at io.vertx.core.http.impl.HttpServerRequestImpl.checkEnded(HttpServerRequestImpl.java:438)
	at io.vertx.core.http.impl.HttpServerRequestImpl.handler(HttpServerRequestImpl.java:203)
	at io.vertx.ext.web.impl.HttpServerRequestWrapper.handler(HttpServerRequestWrapper.java:39)
	at io.vertx.ext.web.handler.impl.BodyHandlerImpl.handle(BodyHandlerImpl.java:73)

After investigation I found that all route checking the roles are registered with the order 0 because of the following line:

route.order(definition.getOrder()); // same order as following handler

However for the route serving the content there is a check avoiding this case:

if (definition.getOrder() != 0) {
route.order(definition.getOrder());
}

I found a workarround by adding the following lines before calling RestRouter.register which make sure that parsing the body is done before anything else:

router.route().order(Integer.MIN_VALUE)
                .handler(BodyHandler.create())

Best,

Have less inherited dependencies

When adding the rest.vertx dependency through maven, it add 3Mb+ dependencies that seem not useful.

At the moment, I import it with a lot of exclusions, but it would be better to have theses dependencies marked as optional in the project:

        <dependency>
            <groupId>com.zandero</groupId>
            <artifactId>rest.vertx</artifactId>
            <version>0.8.1</version>
            <exclusions>
                <exclusion>
                    <groupId>io.vertx</groupId>
                    <artifactId>vertx-rx-java</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.jasypt</groupId>
                    <artifactId>jasypt</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>commons-validator</groupId>
                    <artifactId>commons-validator</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

Rest return CompletableFuture

This is not quite a bug, but when I use the example of return Future, the return is actually the future object, not the Dummy content, I have modified this way, it is working and return the the json. However, I don't know whether I am blocking vertx main thread or not. Could you please advice?

        @POST
	@Path("/save")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	public Product save(Product product) throws InterruptedException, ExecutionException {
		CompletableFuture<Product> result = productService.saveProduct(product);
		result.thenAccept(p -> {
			System.out.println("Product: " + p);
		});
		return result.get();

	}

Proposed enhancement: Provide a way to register exception handlers for specific exceptions

The exception handling is very good but I think It'd be great if one could register ExceptionHandlers for specific exceptions. A proposal is to add a method to the ExceptionHandler interface that returns the list of exceptions it handles. If the list returned is null/empty, then it would handle any exception, otherwise the ExceptionHandler would handle the specific exception.

public interface ExceptionHandler<T extends Throwable> extends HttpResponseWriter<T> {
    default List<Throwable> getHandledExceptions() {
        return Collections.emptyList();
    }
}

For example, I need to raise a RuntimeException (BadRequestException) that I'd like the BadRequestExceptionHandler to handle. I can also raise another RuntimeException (UnauthorizedException) that I'd like the UnauthorizedExceptionHandler to handler. The BadRequestExceptionHandler would return a 400 HTTP status whereas UnauthorizedExceptionHandler would return a 401 HTTP status.

Add Support for Template Engines

In some cases, we need to use template engines even in Rest services. For example, if we want to redirect the user to a third-party server with x-www-form-urlencoded body payload we need to use Javascript for doing this.

QueryParam should be URLDecoded

Hi
I'm trying out rest.vertx (latest version, 0.8.6), so far I like it very much. There's just a small issue related to service methods with QueryParam.

Observation:
Service methods which use @QueryParam receive the URL encoded param, for example "Hello%20World"

Expected:
Standard behaviour would be to get the URL decoded query param, i.e. "Hello World"

Cheers
Thomas

Add support to @ApplicationPath

Would be great to have a base class with eg: @ApplicationPath("v1") so any subclasses will be routed relative to the base class path, eg: /v1/resource1 etc..

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.