There are a couple of callbacks that are the most appropriate for doing validation in. The first is the malformed_request? which runs early in the Finite State Machine, and the second is inside the content type handler, for example from_json.
If malformed_request? returns a truthy value, then a 400 Bad Request will be returned. Unfortunately, at this early stage in the flow, we don't know what the method or the Content-Type are without inspecting the request, and this leads to some very Iffy code.
class OrdersResource < Webmachine::Resource def allowed_methods ["POST", "GET"] end # Iffy! What method? GET doesn't require any validation. def malformed_request? if request.post? # What Content-Type? Very Iffy! if request.headers['Content-Type'] == "application/json" .... end else false end end def content_types_accepted [ ["application/json", :from_json], ["application/xml", :from_xml] ] end def content_types_provided [ ["application/json", :to_json], ["application/xml", :to_xml] ] end def post_is_create? true end def create_path "/orders/#{next_id}" end def from_json order = Order.from_json(request.body.to_s) response.body = order.save(next_id).to_json end def from_xml order = Order.from_xml(request.body.to_s) response.body = order.save(next_id).to_xml end def to_json Order.all.to_json end def to_xml Order.all.to_xml end private def next_id @next_id ||= Order.next_id end end
A more elegant way to handle validation is to do it in a callback where we already know the method and the Content-Type - that is, the handler for the given Content-Type (eg. from_json and from_xml). By returning a 400 from the handler, we stop the state machine flow.
class OrdersResource < Webmachine::Resource def allowed_methods ["POST", "GET"] end # Iffy! What method? GET doesn't require any validation. def malformed_request? if request.post? invalid_create_order_request? else false end end def content_types_accepted [ ["application/json", :from_json], ["application/xml", :from_xml] ] end def content_types_provided [ ["application/json", :to_json], ["application/xml", :to_xml] ] end def post_is_create? true end def create_path "/orders/#{next_id}" end def from_json order = Order.from_json(request.body.to_s) # A bit less Iffy! return json_validation_errors(order) unless order.valid? response.body = order.save(next_id).to_json end # This could use some DRYing up, but you get the point. def from_xml order = Order.from_xml(request.body.to_s) # A bit less Iffy! return xml_validation_errors(order) unless order.valid? response.body = order.save(next_id).to_xml end def to_json Order.all.to_json end def to_xml Order.all.to_xml end private def json_validation_errors(order) response.body = order.validation_errors.to_json 400 end def xml_validation_errors(order) response.body = order.validation_errors.to_xml 400 end def next_id @next_id ||= Order.next_id end end