Package: middletools
MiddletoolsThis is a python library that allows you to integrate middlewares-based system to your project. It contains base tools for creating and running middlewares withasync-awaitstyleInstallationPyPIpython-mpipinstallmiddletoolsGitHubpython-mpipinstallhttps://github.com/deknowny/middlewares/archive/main.zipUsageThe main idea is give an ability just passing the middlewares andinbox/outboxpayload values in a few methods instead of running and saving middlewares state by handStandard case: a function runs RESTful API routers and requires a middleware that checks
a header in client's requestThere are 2 endpoints for an abstractGETandPOSTmethods# Some abstract router@router.get("/")asyncdefroute_get(request):return200,{"response":"some content"}@router.post("/")asyncdefroute_post(request):return201,{"response":"ok"}In the core of web framework you used a function like this that just call all routersclassRouter:......asyncdefcall_routers(self,request):forrouterinself.routers:...# Pass request to routers and check it matchedmiddlewareslibrary allows you easy integrate middleware system to yourcall_routersCreate middleware functionimportmiddletools......# Adding a middleware handler to an abstract@router.add_middlewareasyncdefmy_middleware(request:SomeRequest,call_next:middletools.types.CallNext)->SomeResponse:# Just check if header exists, id not set the default valueif"X-Required-Header"notinrequest.headers:request.header["X-Required-Header"]="default"response=awaitcall_next()returnresponseHere we add a header to client request if clint didn't do it. Thenawait call_next()give control to other middlewares or to ourcall_routershandler and response from this is the valuecall_next()returnscall_routersshould looks like thisimporttypingimportmiddletoolsclassRouter:# You can use generics to describe middleware hand;ermiddlewares:typing.List[middletools.types.MiddlewareHandler[SomeRequest,SomeResponse]]......asyncdefcall_routers(self,request):read_afterwords=awaitmiddletools.read_forewords(*self.middlewares,inbox_value=request)forrouterinself.routers:...# Pass request to routers and check it matchedresponse=...awaitread_afterwords(response)breakmiddlewares.read_forewordsrun middlewares until every one of them give control withawait call_next().
When we do all our stuff and get the router response we can callawait read_afterwords(response)and run all middlewares completely.NotesIf a middleware doesn't callcall_next()it raisesmiddlewares.CallNextNotUsedError. It means that the middleware forcibly decline middlewares handlers and response should be sent immediately without routers running.call_routersshould looks like this:importmiddletoolsasyncdefcall_routers(self,request):try:read_afterwords=awaitmiddletools.read_forewords(*self.middlewares,inbox_value=request)forrouterinself.routers:...# Pass request to routers and check it matchedresponse=...awaitread_afterwords(response)returnresponseexceptmiddletools.CallNextNotUsedError:returnSomeBadResponseBecauseNotRouted(400,"Require a header!")If a middleware doesn't return anything, middlewares dispatching declined forcibly too but after routers handled. (Return nothing means there isn't anyreturnorreturn Noneused). It raisesmiddlewares.NothingReturnedErrorimportmiddletoolsasyncdefcall_routers(self,request):try:read_afterwords=awaitmiddletools.read_forewords(*self.middlewares,inbox_value=request)forrouterinself.routers:...# Pass request to routers and check it matchedresponse=...awaitread_afterwords(response)returnresponseexceptmiddletools.CallNextNotUsedError:returnSomeBadResponseBecauseNotRouted(400,"Require a header!")exceptmiddletools.NothingReturnedError:returnSomeBadResponseBecauseMiddlewareDntReturnResponse(500,"Oops, internal server error")