在 Swift 中解析多部分 SOAP 响应

Posted

技术标签:

【中文标题】在 Swift 中解析多部分 SOAP 响应【英文标题】:Parse a multipart SOAP response in Swift 【发布时间】:2020-04-28 14:35:59 【问题描述】:

我正在尝试从肥皂服务下载文件,并且在成功请求后得到以下内容

响应头

multipart/related; type="application/xop+xml"; boundary="uuid:917b60a9-3089-43ad-a8c2-b4a3c62db98c"; start="<root.message@cxf.apache.org>"; start-info="text/xml"

响应正文

--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:leseDokumentResponse xmlns:ns2="http://webservice/"><return><status>OK</status><dokument><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:c58d3315-9cb6-413a-98a1-5a29671cfdb6-41@cxf.apache.org"/></dokument></return></ns2:leseDokumentResponse></soap:Body></soap:Envelope>
--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <c58d3315-9cb6-413a-98a1-5a29671cfdb6-41@cxf.apache.org>

... binary data ...
--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d--

如何解析文件二进制数据?

我尝试了这些问题,但没有任何效果 Parsing http-multipart response Parse multipart response for image download in ios

【问题讨论】:

来自响应头:boundary="uuid:917b60a9-3089-43ad-a8c2-b4a3c62db98c" 与您在正文中获得的边界不同。这正常吗?或者你打印了2个不同的?否则,使用Data.firstRange(of:)方法,迭代查找所有--uuid:0a679f64-0753-44fe-b627-2267b5b72b1d,即可找到二进制数据。 注意测试,但蒸汽代码 (github.com/vapor/multipart-kit) 对此有用吗? uuid 应该作为示例而不是按原样,因为它与一个请求不同 【参考方案1】:

使用 MultipartKit 可以轻松解析多部分 SOAP 响应

我在AlamofireDataResponse 上创建了一个扩展来处理响应

import Alamofire
import MultipartKit
import Swime

extension DataResponse where Success == Data 

    var boundary: String? 

        let header = response?.allHeaderFields.first(where: 
            $0.key as? String == "Content-Type"
        )

        let scanner = Scanner(string: header?.value as? String ?? "")

        _ = scanner.scanUpToString("boundary=\"")
        _ = scanner.scanString("boundary=\"")
        let boundary = scanner.scanUpToString("\";")

        return boundary
    

    func multipartParts() -> [MultipartPart] 

        guard let boundary = boundary else  return [] 

        guard let data = try? result.get() else  return [] 

        let parser = MultipartParser(boundary: boundary)

        var parts: [MultipartPart] = []
        var headers: HTTPHeaders = [:]
        var body: Data = Data()

        parser.onHeader =  (field, value) in
            headers.replaceOrAdd(name: field, value: value)
        
        parser.onBody =  new in
            body.append(contentsOf: new.readableBytesView)
        
        parser.onPartComplete = 
            let part = MultipartPart(headers: headers, body: body)
            headers = [:]
            body = Data()
            parts.append(part)
        

        do 
            try parser.execute(data)
         catch 
            print(error.localizedDescription)
        

        return parts
    

    func parseMultipart() -> (message: String, files: [Data])? 

        let parts = multipartParts()

        let message = parts.first(where:  $0.id == "<root.message@cxf.apache.org>" )?.body.string ?? ""

        let dataPart = parts.filter  $0.id != "<root.message@cxf.apache.org>" 

        let files = dataPart.compactMap  Data(multipart: $0) 

        return (message, files)
    


extension ByteBuffer 
    var string: String 
        return String(decoding: self.readableBytesView, as: UTF8.self)
    


extension MultipartPart 

    /// Gets or sets the `name` attribute from the part's `"Content-ID"` header.
    public var id: String? 
        get  self.headers.first(name: "Content-ID") 
    

然后可以在这样的数据响应中使用:

let request = SessionManager.request(urlRequest).responseData(completionHandler:  response in

    switch response.result 
        case .failure(let error):
            self.alert(error)

        case .success:

            guard let parts = response.parseMultipart() else  return 

            DispatchQueue.main.async 

                self.loadResponse(message: parts.message, files: parts.files)
        
    
)

消息 id 可能不同,所以我可能会建议检查类型

希望这会对某人有所帮助。

【讨论】:

以上是关于在 Swift 中解析多部分 SOAP 响应的主要内容,如果未能解决你的问题,请参考以下文章

带有 XML 响应和正文的 SOAP 请求 swift 3,Alamofire

在PHP中解析XML SOAP响应

解析 XML SOAP 响应 C#

如何在php中解析soap xml响应并从字符串中获取信息

如何使用pl/sql解析具有href标签的soap响应

如何在iphone中解析soap请求