Add router.rawApi() macro and tests. (#17)
This commit is contained in:
parent
5a828c3ef1
commit
c41bc8aefc
|
@ -23,10 +23,14 @@ type
|
|||
RestRouteKind* {.pure.} = enum
|
||||
None, Handler, Redirect
|
||||
|
||||
RestRouterFlag* {.pure.} = enum
|
||||
Raw
|
||||
|
||||
RestRoute* = object
|
||||
requestPath*: SegmentedPath
|
||||
routePath*: SegmentedPath
|
||||
callback*: RestApiCallback
|
||||
flags*: set[RestRouterFlag]
|
||||
|
||||
RestRouteItem* = object
|
||||
case kind*: RestRouteKind
|
||||
|
@ -37,6 +41,7 @@ type
|
|||
of RestRouteKind.Redirect:
|
||||
redirectPath*: SegmentedPath
|
||||
path: SegmentedPath
|
||||
flags*: set[RestRouterFlag]
|
||||
|
||||
RestRouter* = object
|
||||
patternCallback*: PatternCallback
|
||||
|
@ -50,18 +55,23 @@ proc init*(t: typedesc[RestRouter],
|
|||
routes: initBTree[SegmentedPath, RestRouteItem]())
|
||||
|
||||
proc addRoute*(rr: var RestRouter, request: HttpMethod, path: string,
|
||||
handler: RestApiCallback) {.raises: [Defect].} =
|
||||
flags: set[RestRouterFlag], handler: RestApiCallback) {.
|
||||
raises: [Defect].} =
|
||||
let spath = SegmentedPath.init(request, path, rr.patternCallback)
|
||||
let route = rr.routes.getOrDefault(spath,
|
||||
RestRouteItem(kind: RestRouteKind.None))
|
||||
case route.kind
|
||||
of RestRouteKind.None:
|
||||
let item = RestRouteItem(kind: RestRouteKind.Handler,
|
||||
path: spath, callback: handler)
|
||||
path: spath, flags: flags, callback: handler)
|
||||
rr.routes.add(spath, item)
|
||||
else:
|
||||
raiseAssert("The route is already in the routing table")
|
||||
|
||||
proc addRoute*(rr: var RestRouter, request: HttpMethod, path: string,
|
||||
handler: RestApiCallback) {.raises: [Defect].} =
|
||||
addRoute(rr, request, path, {}, handler)
|
||||
|
||||
proc addRedirect*(rr: var RestRouter, request: HttpMethod, srcPath: string,
|
||||
dstPath: string) {.raises: [Defect].} =
|
||||
let spath = SegmentedPath.init(request, srcPath, rr.patternCallback)
|
||||
|
@ -88,7 +98,7 @@ proc getRoute*(rr: RestRouter,
|
|||
of RestRouteKind.Handler:
|
||||
# Route handler was found
|
||||
let item = RestRoute(requestPath: path, routePath: route.path,
|
||||
callback: route.callback)
|
||||
flags: route.flags, callback: route.callback)
|
||||
return some(item)
|
||||
of RestRouteKind.Redirect:
|
||||
# Route redirection was found, so we perform path transformation
|
||||
|
@ -149,8 +159,9 @@ macro redirect*(router: RestRouter, meth: static[HttpMethod],
|
|||
echo "\n", fromPath, ": ", repr(res)
|
||||
return res
|
||||
|
||||
macro api*(router: RestRouter, meth: static[HttpMethod],
|
||||
path: static[string], body: untyped): untyped =
|
||||
proc processApiCall(router: NimNode, meth: HttpMethod,
|
||||
path: string, flags: set[RestRouterFlag],
|
||||
body: NimNode): NimNode {.compileTime.} =
|
||||
## Define REST API endpoint and implementation.
|
||||
## Input and return parameters are defined using the ``do`` notation.
|
||||
## For example:
|
||||
|
@ -337,8 +348,16 @@ macro api*(router: RestRouter, meth: static[HttpMethod],
|
|||
block:
|
||||
`procBody`
|
||||
|
||||
`router`.addRoute(`methIdent`, `path`, `doMain`)
|
||||
`router`.addRoute(`methIdent`, `path`, `flags`, `doMain`)
|
||||
|
||||
when defined(nimDumpRest):
|
||||
echo "\n", path, ": ", repr(res)
|
||||
return res
|
||||
|
||||
macro api*(router: RestRouter, meth: static[HttpMethod],
|
||||
path: static[string], body: untyped): untyped =
|
||||
return processApiCall(router, meth, path, {}, body)
|
||||
|
||||
macro rawApi*(router: RestRouter, meth: static[HttpMethod],
|
||||
path: static[string], body: untyped): untyped =
|
||||
return processApiCall(router, meth, path, {RestRouterFlag.Raw}, body)
|
||||
|
|
|
@ -44,24 +44,27 @@ proc processRestRequest*[T](server: T,
|
|||
let queryParams = request.query
|
||||
|
||||
let optBody =
|
||||
try:
|
||||
await request.getContentBody()
|
||||
except HttpCriticalError as exc:
|
||||
debug "Unable to obtain request body", uri = $request.uri,
|
||||
peer = $request.remoteAddress(), meth = $request.meth,
|
||||
error_msg = $exc.msg
|
||||
return await request.respond(Http400)
|
||||
except RestBadRequestError as exc:
|
||||
debug "Request has incorrect content type", uri = $request.uri,
|
||||
peer = $request.remoteAddress(), meth = $request.meth,
|
||||
error_msg = $exc.msg
|
||||
return await request.respond(Http400)
|
||||
except CatchableError as exc:
|
||||
warn "Unexpected exception while getting request body",
|
||||
uri = $request.uri, peer = $request.remoteAddress(),
|
||||
meth = $request.meth, error_name = $exc.name,
|
||||
error_msg = $exc.msg
|
||||
return await request.respond(Http400)
|
||||
if RestRouterFlag.Raw notin route.flags:
|
||||
try:
|
||||
await request.getContentBody()
|
||||
except HttpCriticalError as exc:
|
||||
debug "Unable to obtain request body", uri = $request.uri,
|
||||
peer = $request.remoteAddress(), meth = $request.meth,
|
||||
error_msg = $exc.msg
|
||||
return await request.respond(Http400)
|
||||
except RestBadRequestError as exc:
|
||||
debug "Request has incorrect content type", uri = $request.uri,
|
||||
peer = $request.remoteAddress(), meth = $request.meth,
|
||||
error_msg = $exc.msg
|
||||
return await request.respond(Http400)
|
||||
except CatchableError as exc:
|
||||
warn "Unexpected exception while getting request body",
|
||||
uri = $request.uri, peer = $request.remoteAddress(),
|
||||
meth = $request.meth, error_name = $exc.name,
|
||||
error_msg = $exc.msg
|
||||
return await request.respond(Http400)
|
||||
else:
|
||||
none[ContentBody]()
|
||||
|
||||
debug "Serving API request", peer = $request.remoteAddress(),
|
||||
meth = $request.meth, uri = $request.uri,
|
||||
|
|
|
@ -606,6 +606,27 @@ suite "REST API server test suite":
|
|||
finally:
|
||||
await server.closeWait()
|
||||
|
||||
asyncTest "Handle raw requests inside api handler test":
|
||||
var router = RestRouter.init(testValidate)
|
||||
router.rawApi(MethodPost, "/test/post") do () -> RestApiResponse:
|
||||
let contentType = request.headers.getString(ContentTypeHeader)
|
||||
let acceptType = request.headers.getString(AcceptHeaderName)
|
||||
let body = await request.getBody()
|
||||
return
|
||||
RestApiResponse.response(
|
||||
"type[" & contentType & ":" & body.toHex() & "]")
|
||||
|
||||
var sres = RestServerRef.new(router, serverAddress)
|
||||
let server = sres.get()
|
||||
server.start()
|
||||
try:
|
||||
let res = await httpClient(serverAddress, MethodPost, "/test/post",
|
||||
"0123456789", "application/octet-stream")
|
||||
check:
|
||||
res.status == 200
|
||||
res.data == "type[application/octet-stream:30313233343536373839]"
|
||||
finally:
|
||||
await server.closeWait()
|
||||
|
||||
test "Leaks test":
|
||||
check:
|
||||
|
|
Loading…
Reference in New Issue