当前位置: 代码迷 >> Web前端 >> 使用Sinatra创造Soap Web Service
  详细解决方案

使用Sinatra创造Soap Web Service

热度:344   发布时间:2013-11-25 13:22:27.0
使用Sinatra创建Soap Web Service
简单对象访问协议(SOAP)是一种轻量的、简单的、基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议( HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。

本文中得示例是Soap与Http结合的Web service。其实现了一个简单的Echo和Reverse Echo的服务,既请求原文返回和请求倒序返回。就纯技术层面来说,本质上就是post 一个制造xml格式的请求,获得一个xml格式的response的Http Request。

首先,使用xsd明确request和response的xml结构
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://www.xianlinbox.net/echo" xmlns="http://www.xianlinbox.com/echo" xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified">
  <xs:complexType name="EchoMessageType">
    <xs:sequence>
      <xs:element name="Message" type="xs:string" minOccurs="1" />
    </xs:sequence>
  </xs:complexType>
  <xs:element name="EchoRequest" type="EchoMessageType"/>
  <xs:element name="EchoResponse" type="EchoMessageType"/>
  <xs:element name="ReverseEchoRequest" type="EchoMessageType"/>
  <xs:element name="ReverseEchoResponse" type="EchoMessageType"/>
</xs:schema>

然后,创建http服务
require 'bundler'
Bundler.setup
require 'sinatra/base'
	# XML 解析需用到nokogiri
require 'nokogiri'
require 'builder'

class EchoService < Sinatra::Base

  set :root, File.dirname(__FILE__)


  # 添加http basic authentication
  use Rack::Auth::Basic, "Restricted Area" do |username, password|
    username == 'admin' and password == 'admin'
  end

  # 创建Soap消息异常对象
  module SoapFault
    class MustUnderstandError < StandardError
      def fault_code
        "MustUnderstand"
      end
    end

    class ClientError < StandardError
      def fault_code
        "Client"
      end
    end
  end
	# SOAP要求SOAP messages使用Content-Type:text/xml,Sinatra默认的是application/xml,因此需配置。 
  configure do
     mime_type :xml, "text/xml"
  end
  
	#xsd读取request和response的schema, xslt用于从soap message中获取消息体,
  def initialize(*args)
    @xsd = Nokogiri::XML::Schema(File.read("#{File.dirname(__FILE__)}/public/echo_service.xsd"))
    @xslt = Nokogiri::XSLT(File.read("#{File.dirname(__FILE__)}/lib/soap_body.xslt"))
    super
  end

  # SOAP endpoint
  post '/echo_service' do
    begin
      soap_message = Nokogiri::XML(request.body.read)
      # 通过消息头来判断是否是当前服务可处理的消息。
      raise(SoapFault::MustUnderstandError, "SOAP Must Understand Error", "MustUnderstand") if soap_message.root.at_xpath('//soap:Header/*[@soap:mustUnderstand="1" and not(@soap:actor)]', 'soap' => 'http://schemas.xmlsoap.org/soap/envelope/')
      # 获取消息体
      soap_body = @xslt.transform(soap_message)
      # 通过schema验证request的结构是否合法。
      errors = @xsd.validate(soap_body).map { |e| e.message }.join(", ")
      # 如果request格式不正确,抛错。
      raise(SoapFault::ClientError, errors) unless errors == ""
      # 从request中获取该request请求的操作,通过动态分发的方式发送到对应的处理方法。
      self.send(soap_operation_to_method(soap_body), soap_body)
    rescue StandardError => e
      fault_code = e.respond_to?(:fault_code) ? e.fault_code : "Server"
      halt(500, builder(:fault, :locals => {:fault_string => e.message, :fault_code => fault_code}))
    end
  end

  private

  # 解析消息体获取request请求的操作。
  def soap_operation_to_method(soap_body)
    method = soap_body.root.name.sub(/Request$/, '').gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.to_sym
  end

  # Echo服务的具体处理函数
  def echo(soap_body)
    message = soap_body.root.at_xpath('//echo:Message/text()', 'echo' => 'http://www.without-brains.net/echo').to_s
    builder(:echo_response, :locals => {:message => message})
  end

  # ReverseEcho服务的具体处理函数
  def reverse_echo(soap_body)
    message = soap_body.root.at_xpath('//echo:Message/text()', 'echo' => 'http://www.without-brains.net/echo').to_s.reverse!
    builder(:reverse_echo_response, :locals => {:message => message})
  end
end


通过使用builder可以更方便的构建response的内容,
Fault Builder
xml.SOAP(:Envelope, "xmlns:SOAP" => "http://schemas.xmlsoap.org/soap/envelope/") do
  xml.SOAP :Body do
    xml.SOAP :Fault do
      xml.faultcode "SOAP:#{fault_code}"
      xml.faultstring fault_string
    end
  end
end


EchoResponse Builder
xml.SOAP(:Envelope, "xmlns:SOAP" => "http://schemas.xmlsoap.org/soap/envelope/", "xmlns:echo" => "http://www.without-brains.net/echo") do
  xml.SOAP :Body do
    xml.echo :EchoResponse do
      xml.echo(:Message, message)
    end
  end
end


ReverseEchoRespose Builder
xml.SOAP(:Envelope, "xmlns:SOAP" => "http://schemas.xmlsoap.org/soap/envelope/", "xmlns:echo" => "http://www.without-brains.net/echo") do
  xml.SOAP :Body do
    xml.echo :ReverseEchoResponse do
      xml.echo(:Message, message)
    end
  end
end


That's all!
  相关解决方案