You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spyne SOAP服务返回JSON而非XML的问题求助

解决Spyne SOAP服务返回JSON而非XML的问题

核心排查方向

你的测试工具能正常接收XML,但客户端收到JSON,说明问题大概率出在客户端请求头、服务端返回类型定义或Spyne协议强制配置上,以下是具体解决方案:

1. 替换AnyDict为强类型的ComplexModel返回定义

使用AnyDict会让Spyne自动推断序列化方式,容易出现格式不一致的问题。你需要定义与请求结构对应的响应模型,强制Spyne生成符合SOAP规范的XML:

from spyne import ComplexModel, Unicode

# 定义证书子模型
class CertificateResponse(ComplexModel):
    Date = Unicode
    Number = Unicode
    Blanc = Unicode
    CountryCode = Unicode
    ExpirationDate = Unicode
    Canceled = Unicode
    IssuePlace = Unicode
    Inspector = Unicode

# 定义完整响应模型
class Response91Type(ComplexModel):
    GUID = Unicode
    SendDateTime = Unicode
    Certificate = CertificateResponse
    Status = Unicode
    StatusCode = Unicode
    Inspector = Unicode

# 修改服务方法的返回类型
class RussianIntegrationApiView(ServiceBase):
    @rpc(Request91Type,
         _returns=Response91Type,
         _out_variable_name="Response91")
    def Request91(ctx, request91type):
        # 原有逻辑不变,最后返回对应结构的对象或字典
        return {
            "GUID": request91_dict['GUID'] or None,
            "SendDateTime": request91_dict['SendDateTime'] or None, 
            "Certificate": {
                "Date": request91_dict['Certificate']['Date'] or None,
                "Number": request91_dict['Certificate']['Number'] or None,
                "Blanc": request91_dict['Certificate']['Blanc'] or None,
                "CountryCode": request91_dict['Certificate']['CountryCode'] or None,
                "ExpirationDate": request91_dict['Certificate']['ExpirationDate'] or None,
                "Canceled": request91_dict['Certificate']['Canceled'] or None,
                "IssuePlace": request91_dict['Certificate']['IssuePlace'] or None,
                "Inspector": request91_dict['Certificate']['Inspector'] or None,
            },
            "Status": request91_dict['Status'] or None,
            "StatusCode": request91_dict['StatusCode'] or None,
            "Inspector": request91_dict['Inspector'] or None,
        }

2. 强制服务端输出XML,忽略客户端Accept头

确保Spyne的SOAP协议强制返回XML,避免根据客户端请求头自动切换格式:

# 修改Application初始化,明确设置out_protocol的content_type
application = Application([RussianIntegrationApiView],
                          'https://argusgate.fitorf.ru/srv/ws/uz3',
                          name='UZ3SoapBinding',
                          in_protocol=Soap11(validator='soft'),
                          out_protocol=Soap11(content_type='text/xml; charset=utf-8'))

3. 检查客户端请求头配置

让客户端确认以下请求头是否正确设置:

  • Content-Type: 必须为text/xml; charset=utf-8(SOAP 1.1)或application/soap+xml; charset=utf-8(SOAP 1.2)
  • Accept: 包含text/xml或application/soap+xml,而非仅application/json
  • SOAPAction: 对应服务方法的SOAP动作(可从WSDL中获取)

如果客户端发送的请求头偏向JSON格式,Spyne可能会错误地返回JSON响应。

4. 调试服务端实际输出

在服务端添加日志,确认实际返回的内容和响应头:

# 修改on_method_return_string监听器
def on_method_return_string(ctx, method_return_string):
    logging.info(f"Response Content-Type: {ctx.out_protocol.content_type}")
    logging.info(f"Response Body: {method_return_string}")

# 或者在dispatcher中添加日志
@csrf_exempt
def russian_service_dispatcher(request):
    if request.get_full_path() in ('/UzGosKarantinWebService/russian/?wsdl',
                                   '/UzGosKarantinWebService/russian?wsdl'):
        return get_wsdl_file_russian(request)
    # 打印客户端请求头
    logging.info(f"Client Accept Header: {request.META.get('HTTP_ACCEPT')}")
    logging.info(f"Client Content-Type: {request.META.get('CONTENT_TYPE')}")
    response = russian_api_service(request)
    # 打印服务端响应头
    logging.info(f"Server Response Content-Type: {response['Content-Type']}")
    return response

通过日志可以明确是服务端确实返回了JSON,还是客户端对XML做了错误解析。

5. 确认WSDL与服务定义一致

检查你提供的russian.wsdl文件,确保其中定义的响应类型与你服务中返回的模型结构完全匹配,客户端可能依赖WSDL的定义来解析响应,如果WSDL定义与实际返回结构不符,可能导致解析错误(比如误以为是JSON)。

内容的提问来源于stack exchange,提问作者Shokhrukh Shodiev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 23:15:40