valum icon indicating copy to clipboard operation
valum copied to clipboard

Problems with basepath

Open devnix opened this issue 6 years ago • 4 comments

Hi again!

I'm trying to build the skeleton of my app and I'm having a bad time with subroutes. I've tried several ways to implement it, and the basepath options seems the most modular approach.

I don't have any clue why any of my routes are matching. My current code is the following:

using Valum;
using Valum.ContentNegotiation;
using VSGI;

namespace ValaBB
{
    public class Application : Object
    {
        public static int main (string[] args)
        {
            var app = new Router();

            app.use(basic());
            app.use(accept("text/html"));

            app.use (basepath ("/", new ValaBB.Controller.IndexController().handle));
            app.use (basepath ("/admin", new ValaBB.Controller.AdminIndexController().handle));

            return Server.@new("http", handler: app).run(args);
        }
    }
}
using Valum;
using VSGI;

namespace ValaBB.Controller
{
	class IndexController : Router
	{
		construct {
			get ("/", index);
			get ("/test", test);
		}

		int counter = 0;

		public bool index(Request request, Response response, NextCallback next, Context context) throws Error
		{
			return response.expand_utf8("Counter: %s".printf(this.counter++.to_string()));
		}

		public bool test(Request request, Response response, NextCallback next, Context context) throws Error
		{
			return response.expand_utf8("Test: %s".printf(this.counter++.to_string()));
		}
	}
}
using Valum;
using VSGI;

namespace ValaBB.Controller
{
	class AdminIndexController : Router
	{
		construct
		{
			rule(Method.GET | Method.POST, "/", index);
			rule(Method.GET | Method.POST, "/test", index);
		}

		int counter = 0;

		public bool index(Request request, Response response, NextCallback next, Context context) throws Error
		{
			return response.expand_utf8("Counter: %s".printf(this.counter++.to_string()));
		}
	}
}

By the way, thank you so much for the framework and your time! I'm impatient to build something to give back to the community :-)

devnix avatar Mar 23 '19 02:03 devnix

The basepath for your index controller should be an empty string, otherwise it will match // in the controller.

Does the admin endpoints work as expected?

arteymix avatar Mar 23 '19 21:03 arteymix

Ok, now I've registered the routes like this:

app.use(basepath("/admin", new ValaBB.Controller.Admin.IndexController().handle));
app.use(basepath("", new ValaBB.Controller.IndexController().handle));

and the both routers are working. But if I register the routes like this (which for my mind it has more sense, specially if the application grows), the admin router does not work:

app.use(basepath("", new ValaBB.Controller.IndexController().handle));
app.use(basepath("/admin", new ValaBB.Controller.Admin.IndexController().handle));

Is it the expected way to work? I really find it kinda confusing. I would expect to write something like this:

app.use(basepath("", new ValaBB.Controller.IndexController().handle));
app.use(basepath("/preferences", new ValaBB.Controller.PreferencesController().handle));
app.use(basepath("/user", new ValaBB.Controller.UserController().handle));
app.use(basepath("/category", new ValaBB.Controller.CategoryController().handle));
app.use(basepath("/thread", new ValaBB.Controller.ThreadController().handle));
app.use(basepath("/admin", new ValaBB.Controller.Admin.IndexController().handle));
app.use(basepath("/admin/user", new ValaBB.Controller.Admin.UserController().handle));
app.use(basepath("/admin/category", new ValaBB.Controller.Admin.CategoryController().handle));
app.use(basepath("/admin/thread", new ValaBB.Controller.Admin.ThreadController().handle));

and I would be forced to write somethink similar to this:

app.use(basepath("/admin/user", new ValaBB.Controller.Admin.UserController().handle));
app.use(basepath("/admin/category", new ValaBB.Controller.Admin.CategoryController().handle));
app.use(basepath("/admin/thread", new ValaBB.Controller.Admin.ThreadController().handle));
app.use(basepath("/admin", new ValaBB.Controller.Admin.IndexController().handle));
app.use(basepath("/preferences", new ValaBB.Controller.PreferencesController().handle));
app.use(basepath("/user", new ValaBB.Controller.UserController().handle));
app.use(basepath("/category", new ValaBB.Controller.CategoryController().handle));
app.use(basepath("/thread", new ValaBB.Controller.ThreadController().handle));
app.use(basepath("", new ValaBB.Controller.IndexController().handle));

devnix avatar Mar 24 '19 15:03 devnix

In basepath, unmatched route should result in a call to next by the forward callback (here the Router.handle), but it only implements the VSGI.Handler interface which does not have the capability of forwarding requests.

You could fix this with:

app.use (basepath ("", (req, res, next, ctx) => {
    if (!index_router.handle (req, res)) { // false is returned here if the request has not been "handled"
       return next (req, res, next, ctx);
    } else {
       return true;
   }
});

Basepath could be modified to adopt this behaviour, which would be more natural to work with callback that does not implement the middleware signature.

arteymix avatar Mar 25 '19 15:03 arteymix

The alternative would be to bind those index routes directly on the root router.

In the future, I wanted rework Router to work with classes implementing the Middleware interface instead of callbacks and having Middleware-wrapped callbacks, which would allow for a fully asynchronous routing.

Have a look at Template-GLib! I think it has gotten very good now since it's part of Builder and well maintained.

arteymix avatar Mar 25 '19 15:03 arteymix