Skip to content

Commit d47c4ec

Browse files
author
zhansheng.lzs
committed
fix: resolve UTF-8 encoding issues in HTTP request bodies
- Add charset=utf-8 to Content-Type and Accept headers - Serialize JSON with ensure_ascii=False for proper UTF-8 encoding - Fix body size calculation to prevent Content-Length mismatches - Apply changes to sync HTTP, async HTTP, WebSocket, and form data paths - Add comprehensive unit and integration tests This fix reduces request body size by 57.5% for non-ASCII content (e.g., 12.41MB → 5.27MB for websearch.json test data) and ensures compatibility with servers that enforce strict body size limits. Closes issue with large UTF-8 payloads exceeding server limits.
1 parent f4ad802 commit d47c4ec

8 files changed

Lines changed: 600 additions & 20 deletions

File tree

dashscope/api_entities/aiohttp_request.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def __init__(
5757
self.async_request = async_request
5858
self._external_aio_session = session
5959
self.headers = {
60-
"Accept": "application/json",
60+
"Accept": "application/json; charset=utf-8",
6161
"Authorization": f"Bearer {api_key}",
6262
"Cache-Control": "no-cache",
6363
**self.headers, # type: ignore[has-type]
@@ -70,7 +70,7 @@ def __init__(
7070
}
7171
self.method = http_method
7272
if self.method == HTTPMethod.POST:
73-
self.headers["Content-Type"] = "application/json"
73+
self.headers["Content-Type"] = "application/json; charset=utf-8"
7474

7575
self.stream = stream
7676
if self.stream:
@@ -286,10 +286,13 @@ async def _handle_request(self):
286286
timeout=request_timeout,
287287
)
288288
else:
289+
body = json.dumps(obj, ensure_ascii=False).encode(
290+
"utf-8",
291+
)
289292
response = await session.request(
290293
"POST",
291294
url=self.url,
292-
json=obj,
295+
data=body,
293296
headers=self.headers,
294297
timeout=request_timeout,
295298
)

dashscope/api_entities/api_request_data.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,14 @@ def get_aiohttp_payload(self):
8383
form.add_field(key, value)
8484
form.add_field("model", data["model"])
8585
if "input" in data:
86-
form.add_field("input", json.dumps(data["input"]))
87-
form.add_field("parameters", json.dumps(data["parameters"]))
86+
form.add_field(
87+
"input",
88+
json.dumps(data["input"], ensure_ascii=False),
89+
)
90+
form.add_field(
91+
"parameters",
92+
json.dumps(data["parameters"], ensure_ascii=False),
93+
)
8894
return True, form()
8995
# pylint: disable=unreachable,pointless-string-statement
9096
"""

dashscope/api_entities/http_request.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def __init__(
9090
self._external_session = None
9191
self._external_aio_session = None
9292
self.headers: Dict = {
93-
"Accept": "application/json",
93+
"Accept": "application/json; charset=utf-8",
9494
"Authorization": f"Bearer {api_key}",
9595
**self.headers,
9696
}
@@ -115,7 +115,7 @@ def __init__(
115115
}
116116
self.method = http_method
117117
if self.method == HTTPMethod.POST:
118-
self.headers["Content-Type"] = "application/json"
118+
self.headers["Content-Type"] = "application/json; charset=utf-8"
119119

120120
self.stream = stream
121121
if self.stream:
@@ -194,10 +194,13 @@ async def _handle_aio_request(self): # pylint: disable=too-many-branches
194194
timeout=request_timeout,
195195
)
196196
else:
197+
body = json.dumps(obj, ensure_ascii=False).encode(
198+
"utf-8",
199+
)
197200
response = await session.request(
198201
"POST",
199202
url=self.url,
200-
json=obj,
203+
data=body,
201204
headers=self.headers,
202205
timeout=request_timeout,
203206
)
@@ -483,10 +486,13 @@ def _handle_request(self): # pylint: disable=too-many-branches
483486
)
484487
else:
485488
logger.debug("Request body: %s", obj)
489+
body = json.dumps(obj, ensure_ascii=False).encode(
490+
"utf-8",
491+
)
486492
response = session.post(
487493
url=self.url,
488494
stream=self.stream,
489-
json=obj,
495+
data=body,
490496
headers={**self.headers},
491497
timeout=self.timeout,
492498
)

dashscope/api_entities/websocket_request.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def __init__(
7373

7474
self.headers = {
7575
"Authorization": f"bearer {api_key}",
76-
**self.headers,
76+
**self.headers, # type: ignore[has-type]
7777
}
7878

7979
self.task_headers = {
@@ -422,4 +422,4 @@ async def _check_websocket_unexpected_message(self, msg):
422422

423423
def _build_up_message(self, headers, payload):
424424
message = {"header": headers, "payload": payload}
425-
return json.dumps(message)
425+
return json.dumps(message, ensure_ascii=False)

dashscope/client/base_api.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Copyright (c) Alibaba, Inc. and its affiliates.
33
import asyncio
44
import collections
5+
import json
56
import time
67
from http import HTTPStatus
78
from typing import Any, Dict, Iterator, List, Union
@@ -1266,11 +1267,13 @@ def call(
12661267
flattened_output = kwargs.pop("flattened_output", False)
12671268
with requests.Session() as session:
12681269
logger.debug("Starting request: %s", url)
1270+
body = json.dumps(data, ensure_ascii=False).encode("utf-8")
12691271
response = session.post(
12701272
url,
1271-
json=data,
1273+
data=body,
12721274
stream=stream,
12731275
headers={
1276+
"Content-Type": "application/json; charset=utf-8",
12741277
**_workspace_header(workspace),
12751278
**default_headers(api_key),
12761279
**kwargs.pop("headers", {}),
@@ -1295,7 +1298,7 @@ class UpdateMixin:
12951298
def update(
12961299
cls,
12971300
target: str,
1298-
json: object,
1301+
json_body: object,
12991302
api_key: str = None,
13001303
path: str = None,
13011304
workspace: str = None,
@@ -1306,7 +1309,7 @@ def update(
13061309
13071310
Args:
13081311
target (str): The target to update.
1309-
json (object): The create request json body.
1312+
json_body (object): The create request json body.
13101313
api_key (str, optional): The api api_key, if not present,
13111314
will get by default rule(TODO: api key doc). Defaults to None.
13121315
@@ -1329,11 +1332,13 @@ def update(
13291332
flattened_output = kwargs.pop("flattened_output", False)
13301333
with requests.Session() as session:
13311334
logger.debug("Starting request: %s", url)
1335+
body = json.dumps(json_body, ensure_ascii=False).encode("utf-8")
13321336
if method == "post":
13331337
response = session.post(
13341338
url,
1335-
json=json,
1339+
data=body,
13361340
headers={
1341+
"Content-Type": "application/json; charset=utf-8",
13371342
**_workspace_header(workspace),
13381343
**default_headers(api_key),
13391344
**kwargs.pop("headers", {}),
@@ -1343,8 +1348,9 @@ def update(
13431348
else:
13441349
response = session.patch(
13451350
url,
1346-
json=json,
1351+
data=body,
13471352
headers={
1353+
"Content-Type": "application/json; charset=utf-8",
13481354
**_workspace_header(workspace),
13491355
**default_headers(api_key),
13501356
**kwargs.pop("headers", {}),
@@ -1360,7 +1366,7 @@ class PutMixin:
13601366
def put(
13611367
cls,
13621368
target: str,
1363-
json: object,
1369+
json_body: object,
13641370
path: str = None,
13651371
api_key: str = None,
13661372
workspace: str = None,
@@ -1370,7 +1376,7 @@ def put(
13701376
13711377
Args:
13721378
target (str): The target to update.
1373-
json (object): The create request json body.
1379+
json_body (object): The create request json body.
13741380
api_key (str, optional): The api api_key, if not present,
13751381
will get by default rule(TODO: api key doc). Defaults to None.
13761382
@@ -1392,10 +1398,12 @@ def put(
13921398
)
13931399
with requests.Session() as session:
13941400
logger.debug("Starting request: %s", url)
1401+
body = json.dumps(json_body, ensure_ascii=False).encode("utf-8")
13951402
response = session.put(
13961403
url,
1397-
json=json,
1404+
data=body,
13981405
headers={
1406+
"Content-Type": "application/json; charset=utf-8",
13991407
**_workspace_header(workspace),
14001408
**default_headers(api_key),
14011409
**kwargs.pop("headers", {}),

dashscope/common/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def default_headers(api_key: str = None) -> Dict[str, str]:
166166
if api_key is None:
167167
api_key = get_default_api_key()
168168
headers["Authorization"] = f"Bearer {api_key}"
169-
headers["Accept"] = "application/json"
169+
headers["Accept"] = "application/json; charset=utf-8"
170170
return headers
171171

172172

0 commit comments

Comments
 (0)