⚗️ add support for data json file

This commit is contained in:
yanyongyu 2021-12-24 23:15:28 +08:00
parent 75e2ca77df
commit 80258fe2d4
5 changed files with 90 additions and 13 deletions

View File

@ -2,6 +2,8 @@ import abc
from enum import Enum from enum import Enum
from http.cookiejar import Cookie, CookieJar from http.cookiejar import Cookie, CookieJar
from typing import ( from typing import (
IO,
Any,
Dict, Dict,
List, List,
Tuple, Tuple,
@ -9,7 +11,6 @@ from typing import (
Mapping, Mapping,
Iterator, Iterator,
Optional, Optional,
Sequence,
MutableMapping, MutableMapping,
) )
@ -19,21 +20,34 @@ from multidict import CIMultiDict
RawURL = Tuple[bytes, bytes, Optional[int], bytes] RawURL = Tuple[bytes, bytes, Optional[int], bytes]
SimpleQuery = Union[str, int, float] SimpleQuery = Union[str, int, float]
QueryVariable = Union[SimpleQuery, Sequence[SimpleQuery]] QueryVariable = Union[SimpleQuery, List[SimpleQuery]]
QueryTypes = Union[ QueryTypes = Union[
None, str, Mapping[str, QueryVariable], Sequence[Tuple[str, QueryVariable]] None, str, Mapping[str, QueryVariable], List[Tuple[str, QueryVariable]]
] ]
HeaderTypes = Union[ HeaderTypes = Union[
None, None,
CIMultiDict[str], CIMultiDict[str],
Dict[str, str], Dict[str, str],
Sequence[Tuple[str, str]], List[Tuple[str, str]],
] ]
ContentTypes = Union[str, bytes, None]
CookieTypes = Union[None, "Cookies", CookieJar, Dict[str, str], List[Tuple[str, str]]] CookieTypes = Union[None, "Cookies", CookieJar, Dict[str, str], List[Tuple[str, str]]]
ContentTypes = Union[str, bytes, None]
DataTypes = Union[dict, None]
FileContent = Union[IO[bytes], bytes]
FileType = Tuple[Optional[str], FileContent, Optional[str]]
FileTypes = Union[
# file (or bytes)
FileContent,
# (filename, file (or bytes))
Tuple[Optional[str], FileContent],
# (filename, file (or bytes), content_type)
FileType,
]
FilesTypes = Union[Dict[str, FileTypes], List[Tuple[str, FileTypes]], None]
class HTTPVersion(Enum): class HTTPVersion(Enum):
H10 = "1.0" H10 = "1.0"
@ -51,6 +65,9 @@ class Request:
headers: HeaderTypes = None, headers: HeaderTypes = None,
cookies: CookieTypes = None, cookies: CookieTypes = None,
content: ContentTypes = None, content: ContentTypes = None,
data: DataTypes = None,
json: Any = None,
files: FilesTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11, version: Union[str, HTTPVersion] = HTTPVersion.H11,
timeout: Optional[float] = None, timeout: Optional[float] = None,
): ):
@ -93,6 +110,19 @@ class Request:
# body # body
self.content: ContentTypes = content self.content: ContentTypes = content
self.data: DataTypes = data
self.json: Any = json
self.files: Optional[List[Tuple[str, FileType]]] = None
if files:
self.files = []
files_ = files.items() if isinstance(files, dict) else files
for name, file_info in files_:
if not isinstance(file_info, tuple):
self.files.append((name, (None, file_info, None)))
elif len(file_info) == 2:
self.files.append((name, (file_info[0], file_info[1], None)))
else:
self.files.append((name, file_info)) # type: ignore
def __repr__(self) -> str: def __repr__(self) -> str:
class_name = self.__class__.__name__ class_name = self.__class__.__name__

View File

@ -35,11 +35,17 @@ class Mixin(ForwardMixin):
raise RuntimeError(f"Unsupported HTTP version: {setup.version}") raise RuntimeError(f"Unsupported HTTP version: {setup.version}")
timeout = aiohttp.ClientTimeout(setup.timeout) timeout = aiohttp.ClientTimeout(setup.timeout)
files = None
if setup.files:
files = aiohttp.FormData()
for name, file in setup.files:
files.add_field(name, file[1], content_type=file[2], filename=file[0])
async with aiohttp.ClientSession(version=version) as session: async with aiohttp.ClientSession(version=version) as session:
async with session.request( async with session.request(
setup.method, setup.method,
setup.url, setup.url,
data=setup.content, data=setup.content or setup.data or files,
json=setup.json,
headers=setup.headers, headers=setup.headers,
timeout=timeout, timeout=timeout,
) as response: ) as response:

View File

@ -11,17 +11,17 @@ FastAPI 驱动适配
""" """
import logging import logging
from typing import List, Callable, Optional from typing import Any, List, Tuple, Callable, Optional
import uvicorn import uvicorn
from pydantic import BaseSettings from pydantic import BaseSettings
from fastapi.responses import Response from fastapi.responses import Response
from fastapi import FastAPI, Request, status from fastapi import FastAPI, Request, UploadFile, status
from starlette.websockets import WebSocket, WebSocketState from starlette.websockets import WebSocket, WebSocketState
from ._model import FileTypes
from nonebot.config import Env from nonebot.config import Env
from nonebot.typing import overrides from nonebot.typing import overrides
from nonebot.utils import escape_tag
from nonebot.config import Config as NoneBotConfig from nonebot.config import Config as NoneBotConfig
from nonebot.drivers import Request as BaseRequest from nonebot.drivers import Request as BaseRequest
from nonebot.drivers import WebSocket as BaseWebSocket from nonebot.drivers import WebSocket as BaseWebSocket
@ -238,12 +238,36 @@ class Driver(ReverseDriver):
request: Request, request: Request,
setup: HTTPServerSetup, setup: HTTPServerSetup,
) -> Response: ) -> Response:
json: Any = None
try:
json = await request.json()
except Exception:
pass
data: Optional[dict] = None
files: Optional[List[Tuple[str, FileTypes]]] = None
try:
form = await request.form()
data = {}
files = []
for key, value in form.multi_items():
if isinstance(value, UploadFile):
files.append(
(key, (value.filename, value.file, value.content_type))
)
else:
data[key] = value
except Exception:
pass
http_request = BaseRequest( http_request = BaseRequest(
request.method, request.method,
str(request.url), str(request.url),
headers=request.headers.items(), headers=request.headers.items(),
cookies=request.cookies, cookies=request.cookies,
content=await request.body(), content=await request.body(),
data=data,
json=json,
files=files,
version=request.scope["http_version"], version=request.scope["http_version"],
) )

View File

@ -30,6 +30,9 @@ class Mixin(ForwardMixin):
setup.method, setup.method,
str(setup.url), str(setup.url),
content=setup.content, content=setup.content,
data=setup.data,
json=setup.json,
files=setup.files,
headers=tuple(setup.headers.items()), headers=tuple(setup.headers.items()),
timeout=setup.timeout, timeout=setup.timeout,
) )

View File

@ -8,15 +8,14 @@ Quart 驱动适配
https://pgjones.gitlab.io/quart/index.html https://pgjones.gitlab.io/quart/index.html
""" """
from typing import List, TypeVar, Callable, Optional, Coroutine from typing import List, Tuple, TypeVar, Callable, Optional, Coroutine
import uvicorn import uvicorn
from pydantic import BaseSettings from pydantic import BaseSettings
from ._model import FileTypes
from nonebot.config import Env from nonebot.config import Env
from nonebot.log import logger
from nonebot.typing import overrides from nonebot.typing import overrides
from nonebot.utils import escape_tag
from nonebot.config import Config as NoneBotConfig from nonebot.config import Config as NoneBotConfig
from nonebot.drivers import Request as BaseRequest from nonebot.drivers import Request as BaseRequest
from nonebot.drivers import WebSocket as BaseWebSocket from nonebot.drivers import WebSocket as BaseWebSocket
@ -24,9 +23,9 @@ from nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup
try: try:
from quart import request as _request from quart import request as _request
import werkzeug.exceptions as exceptions
from quart import websocket as _websocket from quart import websocket as _websocket
from quart import Quart, Request, Response from quart import Quart, Request, Response
from quart.datastructures import FileStorage
from quart import Websocket as QuartWebSocket from quart import Websocket as QuartWebSocket
except ImportError: except ImportError:
raise ValueError("Please install Quart by using `pip install nonebot2[quart]`") raise ValueError("Please install Quart by using `pip install nonebot2[quart]`")
@ -213,6 +212,18 @@ class Driver(ReverseDriver):
async def _handle_http(self, setup: HTTPServerSetup) -> Response: async def _handle_http(self, setup: HTTPServerSetup) -> Response:
request: Request = _request request: Request = _request
json = None
if request.is_json:
json = await request.get_json()
data = await request.form
files_dict = await request.files
files: List[Tuple[str, FileTypes]] = []
key: str
value: FileStorage
for key, value in files_dict.items():
files.append((key, (value.filename, value.stream, value.content_type)))
http_request = BaseRequest( http_request = BaseRequest(
request.method, request.method,
request.url, request.url,
@ -221,6 +232,9 @@ class Driver(ReverseDriver):
content=await request.get_data( content=await request.get_data(
cache=False, as_text=False, parse_form_data=False cache=False, as_text=False, parse_form_data=False
), ),
data=data or None,
json=json,
files=files or None,
version=request.http_version, version=request.http_version,
) )