Thanks for the advice guys. I tried a couple of different things in order to
get it to work including returning `Future[void]` from my `async` procs. Turns
out the error I was getting is just the linter being a tad nit-picky and not
really an error at all.
I did run into another issue which had to do with passing a `proc` as a default
argument to a type constructor. For this issue I tried using the `procvar`
pragma in the proc declaration, in the type signature for the type attributes
which accept the proc, and in the default argument itself. @dom96 had some
great advice for this on irc. Unfortunately, none of it really worked
correctly. The solution that I came up with was to create a variable that
aliases the `proc` that I wanted to pass as the default argument. It's all
working as expected now. I ended up with the following:
import asynchttpserver, asyncdispatch
type
Router* = ref object of RootObj
routes: seq[Route]
Route* = ref object of RootObj
path*: string
get*: proc (req: Request): Future[void]
post*: proc (req: Request): Future[void]
put*: proc (req: Request): Future[void]
delete*: proc(req: Request): Future[void]
Response* = ref object of RootObj
request*: Request
status*: HttpCode
data*: string
headers*: HttpHeaders
proc newRouter*(): Router =
result = Router(
routes: @[]
)
proc notFoundHandler*(req: Request): Future[void] {.async.} =
await req.respond(Http404, "404 Not Found")
#[
Until I can figure out how the hell procvars work, we need
to alias the default request handler to a variable in order
for the Route constructor to accept it as a default argument.
]#
var notFoundHandlerVar = notFoundHandler
proc newRoute*(
path: string,
get, post, put, delete: proc = notFoundHandlerVar
): Route =
result = Route(
path: path,
get: get,
post: post,
put: put,
delete: delete
)
let DEFAULT_HEADERS = newHttpHeaders([
("Content-Type", "text/html"),
("Accept", "text/html"),
("Accept-Charset", "utf-8"),
("Accept-Encoding", "gzip, deflate"),
("Connection", "keep-alive")
])
proc newResponse*(
request: Request,
status: HttpCode = Http200,
data: string = "",
headers: HttpHeaders = DEFAULT_HEADERS
): Response =
result = Response(
request: request,
status: status,
data: data,
headers: headers
)
proc log*(resp: Response): void =
echo $resp.status
proc send*(resp: Response) {.async.} =
log(resp)
await resp.request.respond(
resp.status,
resp.data,
resp.headers
)
proc register*(router: Router, routes: varargs[Route]) =
for route in items(routes):
router.routes.add(route)
proc route*(router: Router, req: Request) {.async.} =
for route in router.routes:
if route.path == req.url.path:
case req.reqMethod:
of HttpGet:
await route.get(req)
else: continue
await notFoundHandler(req)
After I add the mapping for request methods other than `GET`, the next step is
route matching with url path variables. Then I'm thinking I'll implement a more
robust `Response.render` proc to handle rendering source code filters. The
router can be used like this:
import asyncdispatch, asynchttpserver, strutils
import router
from templates import page, index, hello
let server = newAsyncHttpServer()
let myRouter = router.newRouter()
proc indexHandler(req: Request) {.async.} =
var data = page(
title="Home",
contents=index()
)
var resp = newResponse(req, data=data)
await resp.send()
proc helloWorldHandler(req: Request) {.async.} =
var data = page(
title="Hello",
contents=hello("Justin")
)
var resp = newResponse(req, data=data)
await resp.send()
let index = newRoute(
path="/",
get=indexHandler
)
let helloWorld = newRoute(
path="/hello/",
get=helloWorldHandler
)
myRouter.register(index, helloWorld)
proc log(req: Request): void =
echo "[$1] $2" % [$req.reqMethod, req.url.path]
proc cb(req: Request) {.async.} =
log(req)
await myRouter.route(req)
waitFor server.serve(Port(8000), cb)
Does anyone have anything specific they would like to see in a url routing
library? I'm thinking that as I continue to learn Nim, I can build a more
robust api for creating web applications.
At some point I want to try and tackle creating a simple ORM as well. I'm very
familiar with Postresql and SQLite administration, but I will say that my
skills in writing pure SQL are a bit lacking. Therefore, I have a feeling it
will take me quite a while to get the ORM project rolling.