3

背景

我正在尝试使用Savon通过 Magento SOAP API 创建一个类别。在任何人建议之前,我不能使用 Magento SOAP v2 或 REST API。

此代码设置我的客户端并登录:

@client = Savon.client(wsdl: "#{EnvConfig.base_url}/api/soap/?wsdl", log_level: :debug, raise_errors: true, pretty_print_xml: true)

response = @client.call(:login, message: {
  username: EnvConfig.magento_api_user,
  apiKey: EnvConfig.magento_api_key
})

@token = response.to_hash[:login_response][:login_return]

然后我可以调用 magentos 的各种方法。要列出所有产品,我可以致电:

@response = @client.call(:call, message: {session: @token, method: 'catalog_product.list'})

这一切都正常工作,所以上面的任何代码似乎都没有问题。

问题

当我调用catalog_category.create时,我收到了一个错误。如果我只使用 parentID 参数调用该方法:

  @response = @client.call(:call, message: {session: @token, method: 'catalog_category.create', parent_id: 90})

然后 Savon 发送以下 XML:

<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:typens="urn:Magento" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Body>
    <typens:call>
      <session>6939ace91ba26b1da9a21334d7ef2c13</session>
      <method>catalog_category.create</method>
      <parentId>90</parentId>
    </typens:call>
  </env:Body>
</env:Envelope>

这将返回响应Savon::SOAPFault: (103) Attribute "name" is required

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
      <faultcode>103</faultcode>
      <faultstring>Attribute "name" is required.</faultstring>
    </SOAP-ENV:Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

我希望如此,因为API 文档清楚地表明以下属性不是可选的:

  • 父母身份
  • 类别数据
    • 姓名
    • 活跃
    • available_sort_by
    • default_sort_by
    • include_in_menu

因此,如果我拨打包含以下内容的电话name

@response = @client.call(:call, message: {session: @token, method: 'catalog_category.create', parent_id: 90, category_data: {name: 'Fooooo'}})

此 XML 被发送:

<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:typens="urn:Magento" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Body>
    <typens:call>
      <session>6939ace91ba26b1da9a21334d7ef2c13</session>
      <method>catalog_category.create</method>
      <parentId>90</parentId>
      <categoryData>
        <name>Fooooo</name>
      </categoryData>
    </typens:call>
  </env:Body>
</env:Envelope>

但我得到一个Savon::SOAPFault: (SOAP-ENV:Client) Error cannot find parameter错误:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
      <faultcode>SOAP-ENV:Client</faultcode>
      <faultstring>Error cannot find parameter</faultstring>
    </SOAP-ENV:Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

我花了很长时间试图解决这个问题,并尝试发送name参数而不将其包装在 categoryData 以及其他参数中。全部返回相同的错误消息。

至少,最好知道找不到哪个参数!

任何帮助将不胜感激:)

4

1 回答 1

0

我真的找不到一个像样的解决方案。最后,通过自己用这个极其混乱的代码构建 XML 来解决它:

# Builds an XML Request for use with the Magento API
#-------------------------------------------------------------------------------------------------------------
# Usage:
#
# @m = MageAPI.new(token, call, params)   Creates a new MageAPIRequest object with @call, @token, @data and
#                                           @xml_data instance variables.
#
# Expects a hash of params like:
#
#      { parentId: '90',
#        categoryData: {
#          name: 'OMG, does this work?',
#          is_active: 1,
#          available_sort_by: 'position',
#          default_sort_by: 'position',
#          description: 'Foo',
#          url_key: 'url_key',
#          include_in_menu: 1}
#      }
#
#-------------------------------------------------------------------------------------------------------------
class MageAPIRequest
  attr_reader :call, :token, :data, :xml_data
  def initialize(token, call, params = {})
    @call = call
    @token = token
    @data = params
    @xml_data = build_xml
  end

  def to_s
    return @xml_data
  end

  # Build out the xml string to send to the server, excluding the Envelope, body and call tags, which are added by Savon.
  def build_xml
    token = LibXML::XML::Node.new('sessionId', @token)
    token['xsi:type']='xsd:string'
    resourcePath = LibXML::XML::Node.new('resourcePath', @call)
    resourcePath['xsi:type']='xsd:string'

    unless @data.empty?
      args = LibXML::XML::Node.new 'args'
      args['SOAP-ENC:arrayType'] = "xsd:ur-type[#{@data.length}]"
      args['xsi:type'] = 'SOAP-ENC:Array'

      # Build the structure insude 'args', based on the Hash.
      # TODO: Make this recursive, so it can work deeper than one level.
      @data.each do |k,v|
        if v.is_a? Hash

          details = LibXML::XML::Node.new(k.to_s)
          details["xsi:type"] = 'ns2:Map'
          args << details

          v.each do |key,value|

            item_node = LibXML::XML::Node.new("item")

            key_node = LibXML::XML::Node.new("key", key.to_s)
            key_node['xsi:type'] = 'xsd:string'

            if value.is_a? Fixnum
              value_node = LibXML::XML::Node.new("value", value.to_s)
              value_node['xsi:type'] = 'xsd:int'
            elsif value.is_a? Float
              value_node = LibXML::XML::Node.new("value", sprintf("%.2f", value))
              value_node['xsi:type'] = 'xsd:string'
            elsif value.is_a? Array
              value_node = LibXML::XML::Node.new("value")
              value_node['SOAP-ENC:arrayType'] = "xsd:int[#{value.length}]"
              value_node['xsi:type'] = 'SOAP-ENC:Array'

              value.each do |v|
                v_node = LibXML::XML::Node.new("item", v.to_s)
                v_node['xsi:type'] = 'xsd:string'
                v_node['xsi:type'] = 'xsd:int' if v.is_a? Fixnum
                value_node << v_node
              end

            else
              value_node = LibXML::XML::Node.new("value", value.to_s)
              value_node['xsi:type'] = 'xsd:string'
            end

            item_node << key_node
            item_node << value_node
            details << item_node

          end

        else
          pair = LibXML::XML::Node.new(k.to_s, v.to_s)
          # if v.is_a? Fixnum
          #    pair['xsi:type']='xsd:int'
          # else
            pair['xsi:type']='xsd:string'
          # end
          args << pair
        end
      end
    end
    xml_string = token.to_s
    xml_string << resourcePath.to_s
    xml_string << args.to_s
  end


end

然后我在创建请求时使用了它:

request = MageAPIRequest.new(@token, call, params)

远非理想,但它确实有效。值得注意的是,我还必须构建一个解析器来转换如下所示的响应:

  {:balance=>"10",
    :exchange_rate=>"1.000000",
    :response_type=>"ACCEPT",
    :customer_data=>
      {:item=>
        [
          {:key=>"iredeem_member_id", :value=>"110"},
          {:key=>"prefix", :value=>"Ms."},
          {:key=>"lastname", :value=>"Last name"},
          {:key=>"firstname", :value=>"First name"},
          {:key=>"iredeem_points_balance", :value=>"10"},
        ]
      }
  }

变成这样可用的东西:

  {:balance=>"10",
    :exchange_rate=>"1.000000",
    :response_type=>"ACCEPT",
    :customer_data=>
      {:iredeem_member_id=>"110",
       :prefix=>"Ms.",
       :lastname=>"Last name",
       :firstname=>"First name",
       :iredeem_points_balance=>"10"
      }
  }
于 2014-02-25T15:20:15.820 回答