Serializing the errors hash in ActiveModel::Serializer

独自空忆成欢 提交于 2019-12-20 19:46:23

问题


I'm using ActiveModel::Serializer to customize the JSON responses for my API. This works fine in most cases, except when it fails to save a model successfully.

For example,

def create
  def create
    book = Book.new(book_params)
    book.save

    respond_with book, location: nil
  end
end

As I understand it, the respond_with action will basically execute code that looks something like this (in order to generate the response).

  if resource.errors.any?
    render json: {:status => 'failed', :errors => resource.errors}
  else
    render json: {:status => 'created', :object => resource}
  end

This does match up with what I'm seeing - if my model fails to save successfully I see the errors hash as the response. However, I can't figure out how I specify a serializer for the errors hash.

I tried defining an ErrorsSerializer and if I run

ActiveModel::Serializer.serializer_for(book.errors)

in the console it seems to find my serializer, but it doesn't get used. How do I customize the JSON response in this scenario?


回答1:


I believe that the problem in this case is that for the failed status you won't call render with an object, like for created status.

You can use a custom Serializer when calling render, for this case you can probably use something like

if resource.errors.any?
  render serializer: ErrorSerializer, json: {:status => 'failed', :errors => resource.errors}
else
  render json: {:status => 'created', :object => resource}
end

Give it a try and keep us informed of the result :)




回答2:


I found this answer on this blog post ... which starts as such ...

The default serialization of error states in Rails might not be what you want for your app. In that case, it’d be worth knowing how to write a custom serialization format for your needs. In my case, I am trying to match the JSON API format for errors. Here’s a potential implementation…

Example validation error

By default, Rails 4 will return an error serialization that looks like this (for a book model where title should always be present):

{
  "title": [
    "can't be blank"
  ]
}

Create a custom error serializer

In /serializers/error_serializer.rb ...

module ErrorSerializer

  def self.serialize(errors)
    return if errors.nil?

    json = {}
    new_hash = errors.to_hash(true).map do |k, v|
      v.map do |msg|
        { id: k, title: msg }
      end
    end.flatten
    json[:errors] = new_hash
    json
  end
end

Use it in your controller

Now include ErrorSerializer in your controller so that you can do something like this using the errors hash i.e. render: json: ErrorSerializer.serialize(book.errors)

Result

{
  "errors": [
    {
      "id": "title",
      "title": "Title can't be blank"
    }
  ]
}

Read the actual post for the deets.




回答3:


The ErrorsSerializer doesn't work because of how responders create json response for errors:

def json_resource_errors
  { errors: resource.errors }
end

(rails < 4.2 https://github.com/rails/rails/blob/4-1-stable/actionpack/lib/action_controller/metal/responder.rb#L290 for newer rails, responders have been extracted to https://github.com/plataformatec/responders/blob/master/lib/action_controller/responder.rb#L288)

one way of dealing with this is to override this method for responders. Put this code in your config initializers:

# config/initializers/action_controller_responder.rb
module ActionController
  class Responder
    def json_resource_errors
      resource.errors
    end
  end
end

Then your serializer will work for resource errors.




回答4:


The class name of resource.errors is ActiveModel::Errors so you have to define you class as ActiveModel::ErrorsSerializer.

Ref: source code



来源:https://stackoverflow.com/questions/25474294/serializing-the-errors-hash-in-activemodelserializer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!