Tammeuh
Tammeuh

Reputation: 21

Vertx router configuration

I am a novice with vertx so maybe I am doing something wrong. I am trying to implement the following routes:

router.get("/api/users/").handler(this::getUsers);
router.route("/api/users/:username*").handler(this::checkUsername);
router.get("/api/users/:username/").handler(this::getUser);
router.put("/api/users/:username/").handler(this::addUser);
router.get("/api/users/:username/assignments/").handler(this::getAssignments);
router.post("/api/users/:username/assignments/").handler(this::addAssignment);
router.route("/api/users/:username/assignments/:assignmentId/").handler(this::checkAssignmentId);
router.get("/api/users/:username/assignments/:assignmentId/").handler(this::getAssignment);

Is this the correct way to avoid duplicating this logic in all handlers?

I am trying to chain handlers, where the checkUsername handler reads the username parameter from the path, tries to find a corresponding user, and puts that user in the context. If no user is found, a statuscode 400 is returned. Otherwise the next handler is called. I would like to apply the same principle to the assignmentId parameter.

While trying to implement this, I believe I found a problem with the path, more specifically the trailing slash and star. The documentation states that trailing slashes are ignored. This is not the behavior when there is a parameter in the path. In that case the trailing slash matters. If the path definition contains one and the request does not, vertx returns a 404. It does not make a difference whether or not the parameter is at the end of the path or in the middle.

The same goes for paths ending with a star. This functionality does not work when the path contains a parameter.

Upvotes: 1

Views: 3099

Answers (1)

Sebastian Coetzee
Sebastian Coetzee

Reputation: 21

You can use a regular expression to avoid duplication of the checkUsername validation check. What I would do is I would have a method like this to check if the username is valid:

private void checkUsername(RoutingContext routingContext){

    //The "param0" is the capture group of the regular expression. See the routing config below.
    if (isValidUsername(routingContext.request().getParam("param0"))){

        routingContext.next();

    } else {

        routingContext
            .response()
            .setStatusCode(400)
            .end();

    }

}

To check the assignment ID I would do something similar:

private void checkAssignmentId(RoutingContext routingContext){

    if (isValidAssignmentId(routingContext.request().getParam("assignmentId"))){

        routingContext.next();

    } else {

        routingContext
            .response()
            .setStatusCode(400)
            .end();

    }

}

Try to avoid trailing slashes in your paths. I would change the routing handler assignments to be something like this:

router.get("/api/users").handler(this::getUsers);

//By the way, you really want to be using a POST request when adding users just to stick to the principles of REST.
//When you are sending a POST request there is no need to put the username in the URI. You can have it in the request body.
//Please ensure you validate this username using the same validation helper used in your other validations.
router.post("/api/users").handler(this::addUser);

//Use regular expression to match all "/api/users/:username*" URIs
router.routeWithRegex("\\/api\\/users\\/([^\\/]+)").handler(this::checkUsername);
router.get("/api/users/:username").handler(this::getUser);

router.get("/api/users/:username/assignments").handler(this::getAssignments);
router.post("/api/users/:username/assignments").handler(this::addAssignment);

router.route("/api/users/:username/assignments/:assignmentId").handler(this::checkAssignmentId);
router.get("/api/users/:username/assignments/:assignmentId").handler(this::getAssignment);

Upvotes: 1

Related Questions