Rails 4:在将表单发送到soap-xml-webservice/api之前,连接日期和时间字段



在Rails数据库中将两个独立的日期和时间字段连接或连接到一个日期时间条目似乎是一个相当常见的问题。在我的示例中,我试图将:dining_date与:dining_time连接起来,以生成:dining_date_and_time。但是,在谷歌上搜索并尝试了许多类似的stackoverflow解决方案后,我仍然不断收到我试图请求的soap xml Web服务返回的错误消息。

我在SoapUI和我正在使用的Web服务中玩过,我可以看到它对之前使用T非常敏感。这正是它所需的格式:
示例:2015-06-25T18:00:00

也许我在这里没有正确地插入"T"字符,但我尝试了很多组合,但都无法使其发挥作用。

总之,这里有来自Web服务的完整错误跟踪(我在Rails中使用Savon gem作为我的SOAP客户端,到目前为止它运行良好)

Savon::SOAPFault at /
(soap:Client) System.Web.Services.Protocols.SoapException: Server was unable to read request. ---> System.InvalidOperationException: There is an error in XML document (1, 607). ---> System.FormatException: The string '' is not a valid AllXsd value.
   at System.Xml.Schema.XsdDateTime..ctor(String text, XsdDateTimeFlags kinds)
   at System.Xml.XmlConvert.ToDateTime(String s, XmlDateTimeSerializationMode dateTimeOption)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read32_BookReservationRequest(Boolean isNullable, Boolean checkType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read193_BookReservation()
   at Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer20.Deserialize(XmlSerializationReader reader)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
   at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters()
   --- End of inner exception stack trace ---
   at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters()
   at 

我观点的相关部分:

<h1>Book Reservation New Form:</h1>
  <%= form_tag(root_url, :method => :get ) %>
  <div class="field">
  <%= label_tag 'Partner Code (identifies how user has reached this point - restaurant + channel):' %>
  <%= text_field_tag :partner_code %>
  </div>
  <div class="field">
  <%= label_tag 'Restaurant Location ID (which area in restaurant they are seating (dropdown):' %>
  <%= text_field_tag :restaurant_location_id %>
  </div>
  <div class="field">
  <%= label_tag 'Select dining session:' %>
  <%= select_tag(:session_id, options_for_select([['Breakfast'], ['Lunch'], ['Dinner']])) %>
  </div> 
 <div class="field">
     <%= label_tag 'Dining Date:' %>      
     <%= text_field_tag(:dining_date) %>
 </div>
 <div class="field">
     <%= label_tag 'Dining Date:' %>      
     <%= text_field_tag(:dining_time) %>
 </div>
 <div class="field">
   <%= hidden_field_tag :dining_date_and_time %>
 </div>
  <div class="field">
  <%= label_tag 'Party size: (dropdown)' %>
  <%= select_tag(:size, options_for_select([['1 person', 1 ], ['2 people', 2], ['3 people', 3], ['4 people', 4], ['5 people', 5], ['6 people', 6], ['7 people', 7], ['8 people', 8], ['9 people', 9], ['10 people', 10], ['11 people', 11], [ '12 people', 12]])) %>
  </div>
<%= render 'personal_details' %> 
<%= render 'response' %>

我在模型中的最新尝试(也尝试了before_save和before_validation回调):

class BookReservation < ActiveRecord::Base
  attr_reader :confirmation_number, :reservation_id 
  before_create do
      self.dining_date_and_time = "#{dining_date} #{"T"} #{dining_time}"
    end  
  def client
    client = Savon.client(wsdl: "http://example-wsdl-link", follow_redirects: :follow_redirects)
  end   
  def initialize(partner_code, restaurant_location_id, session_id, dining_date_and_time, size, first_name, last_name, email, guest_accepts_email_marketing)
      message = { 'PartnerCode' => partner_code, 'RestaurantLocationId' => restaurant_location_id, 'SessionId' => session_id, 'DiningDateAndTime' => dining_date_and_time, 'Size' => size }
      message.merge!('Booker' => {'UserWithoutALogin' => { 'FirstName' => first_name, 'LastName' => last_name, 'EMail' => email, 'GuestAcceptsEmailMarketingFromPartner' => guest_accepts_email_marketing }})
      response = client.call(:book_reservation, message: message)
      if response.success?
      data = response.to_array(:book_reservation_response).first
      if data
        @confirmation_number = data[:confirmation_number]
        @reservation_id = data[:reservation_id]
      end
      end
  end  
end

控制器:

class BookReservationsController < ApplicationController
  def index    
    if params[:partner_code] && params[:restaurant_location_id] && params[:session_id] && params[:dining_date_and_time] && params[:size] && params[:first_name] && params[:last_name] && params[:email] && params[:guest_accepts_email_marketing]
      @book_reservation = BookReservation.new params[:partner_code], params[:restaurant_location_id], params[:session_id], params[:dining_date_and_time], params[:size], params[:first_name], params[:last_name], params[:email], params[:guest_accepts_email_marketing]
    end
  end 
  def book_reservation_params
    params.require(:book_reservation).permit(:partner_code, :restaurant_location_id, :session_id, :dining_date_and_time, :size, :first_name, :last_name, :email, :guest_accepts_email_marketing, :confirmation_number, :reservation_id, :dining_date, :dining_time)  
  end
end

模式中的表(dining_date_and_time是rails-datetime数据类型)(不需要仅用于基本的soap请求,但最终需要一个数据库来存储这些输入,如果没有它,BookReservation模型将无法作为Activerecord工作):

ActiveRecord::Schema.define(version: 20150625100835) do
  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"
  create_table "book_reservations", force: :cascade do |t|
    t.string   "partner_code"
    t.integer  "restaurant_location_id"
    t.string   "session_id"
    t.datetime "dining_date_and_time"
    t.string   "size"
    t.string   "first_name"
    t.string   "last_name"
    t.string   "email"
    t.boolean  "guest_accepts_email_marketing"
  end
end

提前非常感谢!非常感谢您的帮助!

是的,您的插值不正确。引号中出现的内容都将出现在输出中,因此当您有"#{dining_date} #{"T"} #{dining_time}"时,Rails将在"T"前后插入一个空白。

另外,我看不到您的日期和时间字段有任何格式或验证。用户输入日期"2015-06-25"和时间"18:00:00"的可能性很小。你可能想为你的日期实现一个日期选择器——用户可以从中选择一个很好的弹出日历,你可以使用它来强制执行你想要的格式。对于时间字段,如果不使用某种时间选择器控件,则需要添加一些验证,以确保用户输入正确的时间。这可以在您的模型中完成,也可以在客户端完成,以防止用户在无效时间内提交表单。

一旦输入有效,就可以根据需要使用strftime进行格式化。从我的控制台:

>> foo = "2015-06-25"   #from datepicker in your view
=> "2015-06-25"
>> bar = "2 pm"         #validated time
=> "2 pm"
>> (foo + ' ' + bar).to_datetime.strftime("%Y-%m-%dT%H:%M:%S")     #combine date and time and format as needed
=> "2015-06-25T14:00:00"

好吧,

首先,

此块:

before_create do
  self.dining_date_and_time = "#{dining_date} #{"T"} #{dining_time}"
end

is not reached, before_create fires when you save new object. Otherwise it would have thrown undefined local variable or method 'dining_date' since your model has no chance to get this field.

Instead of checking in index method of controller for persistence of required params, this should be validated in model like that

    class BookReservation < ActiveRecord::Base
      validate :your, :required, :fields, presence: :true
    ...
    end

并且在您的控制器中应该只调用模型。

回到你最初的问题,史蒂夫做出了最好的决定。(foo + ' ' + bar).to_datetime.strftime("%Y-%m-%dT%H:%M:%S")

我冒着风险对你的代码进行了一点重构,并将其组织得更加"轨道式"

http://pastebin.com/CDhReHcA

相关内容

  • 没有找到相关文章