⚗️ 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 http.cookiejar import Cookie, CookieJar
from typing import (
IO,
Any,
Dict,
List,
Tuple,
@ -9,7 +11,6 @@ from typing import (
Mapping,
Iterator,
Optional,
Sequence,
MutableMapping,
)
@ -19,21 +20,34 @@ from multidict import CIMultiDict
RawURL = Tuple[bytes, bytes, Optional[int], bytes]
SimpleQuery = Union[str, int, float]
QueryVariable = Union[SimpleQuery, Sequence[SimpleQuery]]
QueryVariable = Union[SimpleQuery, List[SimpleQuery]]
QueryTypes = Union[
None, str, Mapping[str, QueryVariable], Sequence[Tuple[str, QueryVariable]]
None, str, Mapping[str, QueryVariable], List[Tuple[str, QueryVariable]]
]
HeaderTypes = Union[
None,
CIMultiDict[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]]]
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):
H10 = "1.0"
@ -51,6 +65,9 @@ class Request:
headers: HeaderTypes = None,
cookies: CookieTypes = None,
content: ContentTypes = None,
data: DataTypes = None,
json: Any = None,
files: FilesTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
timeout: Optional[float] = None,
):
@ -93,6 +110,19 @@ class Request:
# body
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:
class_name = self.__class__.__name__

View File

@ -35,11 +35,17 @@ class Mixin(ForwardMixin):
raise RuntimeError(f"Unsupported HTTP version: {setup.version}")
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 session.request(
setup.method,
setup.url,
data=setup.content,
data=setup.content or setup.data or files,
json=setup.json,
headers=setup.headers,
timeout=timeout,
) as response:

View File

@ -11,17 +11,17 @@ FastAPI 驱动适配
"""
import logging
from typing import List, Callable, Optional
from typing import Any, List, Tuple, Callable, Optional
import uvicorn
from pydantic import BaseSettings
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 ._model import FileTypes
from nonebot.config import Env
from nonebot.typing import overrides
from nonebot.utils import escape_tag
from nonebot.config import Config as NoneBotConfig
from nonebot.drivers import Request as BaseRequest
from nonebot.drivers import WebSocket as BaseWebSocket
@ -238,12 +238,36 @@ class Driver(ReverseDriver):
request: Request,
setup: HTTPServerSetup,
) -> 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(
request.method,
str(request.url),
headers=request.headers.items(),
cookies=request.cookies,
content=await request.body(),
data=data,
json=json,
files=files,
version=request.scope["http_version"],
)

View File

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

View File

@ -8,15 +8,14 @@ Quart 驱动适配
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
from pydantic import BaseSettings
from ._model import FileTypes
from nonebot.config import Env
from nonebot.log import logger
from nonebot.typing import overrides
from nonebot.utils import escape_tag
from nonebot.config import Config as NoneBotConfig
from nonebot.drivers import Request as BaseRequest
from nonebot.drivers import WebSocket as BaseWebSocket
@ -24,9 +23,9 @@ from nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup
try:
from quart import request as _request
import werkzeug.exceptions as exceptions
from quart import websocket as _websocket
from quart import Quart, Request, Response
from quart.datastructures import FileStorage
from quart import Websocket as QuartWebSocket
except ImportError:
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:
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(
request.method,
request.url,
@ -221,6 +232,9 @@ class Driver(ReverseDriver):
content=await request.get_data(
cache=False, as_text=False, parse_form_data=False
),
data=data or None,
json=json,
files=files or None,
version=request.http_version,
)