From 260c38d63776a63de6f75102c000efa71e83498b Mon Sep 17 00:00:00 2001 From: yumoqing Date: Mon, 29 Jul 2019 10:01:11 +0800 Subject: [PATCH] add restful support --- ahserver/error.py | 14 ++++ ahserver/globalEnv.py | 5 -- ahserver/processorResource.py | 37 +++++----- ahserver/restful.py | 123 ++++++++++++++++++++++++++++++++++ conf/config.json | 1 + requirements.txt | 3 +- 6 files changed, 160 insertions(+), 23 deletions(-) create mode 100644 ahserver/error.py create mode 100644 ahserver/restful.py diff --git a/ahserver/error.py b/ahserver/error.py new file mode 100644 index 0000000..df0768c --- /dev/null +++ b/ahserver/error.py @@ -0,0 +1,14 @@ +def Error(errno='undefined error',msg='Error'): + return { + "status":"Error", + "data":{ + "message":msg, + "errno":errno + } + } + +def Success(data): + return { + "status":"OK", + "data":data + } diff --git a/ahserver/globalEnv.py b/ahserver/globalEnv.py index 05d444e..2166f1a 100755 --- a/ahserver/globalEnv.py +++ b/ahserver/globalEnv.py @@ -20,10 +20,6 @@ from appPublic.uniqueID import setNode,getID from appPublic.unicoding import unicoding,uDict,uObject from appPublic.Singleton import SingletonDecorator -from sql.crud import _CRUD,CRUD -# from sql.sqlorAPI import DBPools,runSQL,runSQLPaging,runSQLIterator -# from sql.sqlorAPI import getTables,getTableFields,getTablePrimaryKey -# from sql.sqlorAPI import getTableForignKeys,runSQLResultFields from sqlor.dbpools import DBPools @@ -167,7 +163,6 @@ def initEnv(): g.folderInfo = folderInfo g.abspath = abspath g.request2ns = request2ns - g.CRUD = CRUD g.data2xlsx = data2xlsx g.xlsxdata = XLSXData g.openfile = openfile diff --git a/ahserver/processorResource.py b/ahserver/processorResource.py index fc81b39..021bc00 100644 --- a/ahserver/processorResource.py +++ b/ahserver/processorResource.py @@ -19,7 +19,7 @@ from aiohttp.web_routedef import AbstractRouteDef from appPublic.jsonConfig import getConfig from appPublic.MiniI18N import getI18N -from appPublic.dictObject import DictObject +from appPublic.dictObject import DictObject, multiDict2Dict from .baseProcessor import getProcessor from .xlsxdsProcessor import XLSXDataSourceProcessor @@ -27,19 +27,7 @@ from .sqldsProcessor import SQLDataSourceProcessor from .serverenv import ServerEnv from .url2file import Url2File from .filestorage import FileStorage - -def multiDict2Dict(md): - ns = {} - for k,v in md.items(): - ov = ns.get(k,None) - if ov is None: - ns[k] = v - elif type(ov) == type([]): - ov.append(v) - ns[k] = ov - else: - ns[k] = [ov,v] - return ns +from .restful import DBCrud def getHeaderLang(request): al = request.headers.get('Accept-Language') @@ -70,6 +58,10 @@ class ProcessorResource(StaticResource): append_version=append_version) gr = self._routes.get('GET') self._routes.update({'POST':gr}) + self._routes.update({'PUT':gr}) + self._routes.update({'OPTIONS':gr}) + self._routes.update({'DELETE':gr}) + self._routes.update({'TRACE':gr}) self.y_processors = [] self.y_prefix = prefix self.y_directory = directory @@ -90,7 +82,7 @@ class ProcessorResource(StaticResource): real_path = os.path.abspath(rp) return real_path - async def getPostData(self,request): + async def getPostData(self,request: Request) -> dict: reader = await request.multipart() if reader is None: md = await request.post() @@ -161,8 +153,6 @@ class ProcessorResource(StaticResource): return await self.getPostData(request) ns = multiDict2Dict(request.query) return ns - print('** ret=',ret,request.query) - return ns self.y_env.i18n = serveri18n self.y_env.i18nDict = i18nDICT @@ -172,6 +162,19 @@ class ProcessorResource(StaticResource): self.y_env.request2ns = getArgs self.y_env.resource = self path = request.path + config = getConfig() + if config.website.dbrest and path.startswith(config.website.dbrest): + pp = path.split('/')[2:] + if len(pp)<2: + raise HTTPNotFound + dbname = pp[0] + tablename = pp[1] + id = None + if len(pp) > 2: + id = pp[2] + crud = DBCrud(request,dbname,tablename,id=id) + return await crud.dispatch() + for word, handlername in self.y_processors: if path.endswith(word): Klass = getProcessor(handlername) diff --git a/ahserver/restful.py b/ahserver/restful.py new file mode 100644 index 0000000..66cbc74 --- /dev/null +++ b/ahserver/restful.py @@ -0,0 +1,123 @@ +import os +import re + +from aiohttp.web_urldispatcher import StaticResource, _WebHandler, PathLike +from aiohttp.web_urldispatcher import Optional, _ExpectHandler +from aiohttp.web_urldispatcher import Path +from aiohttp.web_response import Response, StreamResponse +from aiohttp.web_exceptions import ( + HTTPException, + HTTPExpectationFailed, + HTTPForbidden, + HTTPMethodNotAllowed, + HTTPNotFound, +) +from aiohttp import web +from aiohttp.web_fileresponse import FileResponse +from aiohttp.web_request import Request +from aiohttp.web_response import Response, StreamResponse +from aiohttp.web_routedef import AbstractRouteDef +from aiohttp.web import json_response + + +from sqlor.crud import CRUD + +from appPublic.dictObject import multiDict2Dict +from appPublic.jsonConfig import getConfig + +from .error import Error,Success + +DEFAULT_METHODS = ('GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE') + +class RestEndpoint: + + def __init__(self): + self.methods = {} + + for method_name in DEFAULT_METHODS: + method = getattr(self, method_name.lower(), None) + if method: + self.register_method(method_name, method) + + def register_method(self, method_name, method): + self.methods[method_name.upper()] = method + + async def dispatch(self): + method = self.methods.get(self.request.method.upper()) + if not method: + raise HTTPMethodNotAllowed('', DEFAULT_METHODS) + + return await method() + + +class DBCrud(RestEndpoint): + def __init__(self, request,dbname,tablename, id=None): + print(f'***{dbname}*{tablename}*{id}************') + super().__init__() + self.dbname = dbname + self.tablename = tablename + self.request = request + self.id = id + try: + self.crud = CRUD(dbname,tablename) + except Exception as e: + print('e=',e) + raise HTTPNotFound + + async def options(self) -> Response: + try: + d = await self.crud.I() + return json_response(Success(d)) + except Exception as e: + print(e) + return json_response(Error(errno='metaerror',msg='get metadata error')) + + async def get(self) -> Response: + """ + query data + """ + try: + ns = multiDict2Dict(self.request.query) + if self.id is not None: + ns['__id'] = self.id + d = await self.crud.R(NS=ns) + return json_response(Success(d)) + except Exception as e: + print(e) + return json_response(Error(errno='search error',msg='search error')) + + async def post(self): + """ + insert data + """ + try: + ns = multiDict2Dict(await self.request.post()) + d = await self.crud.C(ns) + return json_response(Success(d)) + except Exception as e: + print(e) + return json_response(Error(errno='add error',msg='add error')) + + async def put(self): + """ + update data + """ + try: + ns = multiDict2Dict(await self.request.post()) + d = await self.crud.U(ns) + return json_response(Success('')) + except Exception as e: + print(e) + return json_response(Error(errno='update error',msg='update error')) + + async def delete(self, request: Request, instance_id): + """ + delete data + """ + try: + ns = multiDict2Dict(self.request.query) + d = await self.crud.D(ns) + return json_response(Success(d)) + except Exception as e: + print(e) + return json_response(Error(erron='delete error',msg='error')) diff --git a/conf/config.json b/conf/config.json index 325af0a..728eafb 100644 --- a/conf/config.json +++ b/conf/config.json @@ -43,6 +43,7 @@ "index.dspy", "index.md" ], + "dbrest":"/dbs", "visualcoding":{ "default_root":"/samples/vc/test", "userroot":{ diff --git a/requirements.txt b/requirements.txt index 9928594..6d7f481 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,5 @@ sqlor jinja2 ujson openpyxl -#appPublic +appPublic +patterncoding