如何在Ruby on Rails中接收嵌套属性的JSON:API帖子?

时间:2019-05-16 18:58:53

标签: ruby-on-rails reactjs redux json-api

在Rails中是否有一种“标准”的方法来接收(可能嵌套)一个JSON:API POST对象?

JSON:API规范对GET / POST / PUT等使用相同的格式,但是rails似乎需要* _attributes和accepts_nested_attributes_for。这些似乎不兼容。

我觉得我做的事情一定很常见,但是我在查找文档时遇到了麻烦。我想使用React / Redux应用程序,该应用程序使用JSON:API规范与Rails应用程序进行通信。我只是不确定如何处理嵌套关联。

2 个答案:

答案 0 :(得分:1)

您可以active_model_serializer gem的反序列化功能。

来自宝石的docs

class PostsController < ActionController::Base
  def create
    Post.create(create_params)
  end

  def create_params
    ActiveModelSerializers::Deserialization.jsonapi_parse(params, only: [:title, :content, :author])
  end
end

以上内容可与以下JSON API有效负载配合使用:

document = {
  'data' => {
    'id' => 1,
    'type' => 'post',
    'attributes' => {
      'title' => 'Title 1',
      'date' => '2015-12-20'
    },
    'relationships' => {
      'author' => {
        'data' => {
          'type' => 'user',
          'id' => '2'
        }
      },
      'second_author' => {
        'data' => nil
      },
      'comments' => {
        'data' => [{
          'type' => 'comment',
          'id' => '3'
        },{
          'type' => 'comment',
          'id' => '4'
        }]
      }
    }
  }
}

可以在不指定任何选项的情况下解析整个文档:

ActiveModelSerializers::Deserialization.jsonapi_parse(document)
#=>
# {
#   title: 'Title 1',
#   date: '2015-12-20',
#   author_id: 2,
#   second_author_id: nil
#   comment_ids: [3, 4]
# }

答案 1 :(得分:0)

几天前,我在JSON:API存储库上看到了这些冗长的主题讨论#979#795,显然,看来JSON API并没有针对accepts_nested_attributes_for的真正解决方案。

我不知道这是否是更好的解决方案,但是解决该问题的方法是为您的belongs_tohas_many/has_one关联分配路由。

类似的东西:

您的 routes.rb

Rails.application.routes.draw do
  resources :contacts do
    resource :kind, only: [:show]
    resource :kind, only: [:show], path: 'relationships/kind'

    resource :phones, only: [:show]
    resource :phones, only: [:show], path: 'relationships/phones'

    resource :phone, only: [:update, :create, :destroy]
    # These relationships routes is merely a suggestion of a best practice
    resource :phone, only: [:update, :create, :destroy], path: 'relationships/phone'

    resource :address, only: [:show, :update, :create, :destroy]
    resource :address, only: [:show, :update, :create, :destroy], path: 'relationships/address'
  end

  root 'contacts#index'
end

他们实现您的控制器。 遵循上述示例的 phones_controller.rb

class PhonesController < ApplicationController
  before_action :set_contacts

  def update
    phone = Phone.find(phone_params[:id])

    if phone.update(phone_params)
      render json: @contact.phones, status: :created, location: contact_phones_url(@contact.id)
    else
      render json: @contact.errors, status: :unprocessable_entity
    end
  end

  # DELETE /contacts/1/phone
  def destroy
    phone = Phone.find(phone_params[:id])
    phone.destroy
  end

  # POST contacts/1/phone
  def create
    @contact.phones << Phone.new(phone_params)

    if @contact.save
      render json: @contact.phones, status: :created, location: contact_phones_url(@contact.id)
    else
      render json: @contact.errors, status: :unprocessable_entity
    end
  end

  # GET /contacts/1/phones
  def show
    render json: @contact.phones
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_contacts
      @contact = Contact.find(params[:contact_id])
    end

    def phone_params
      ActiveModelSerializers::Deserialization.jsonapi_parse(params)
    end
end

这样做,您应该通常可以通过联系人分别请求电话 POST ,如下所示:

POST在 http:// localhost:3000 / contacts / 1 / phone 上,正文:

{
    "data": {
            "type": "phones",
            "attributes": {
                "number": "(+55) 91111.2222"
            }
    }
}

http:// localhost:3000 / contacts / 1 / phones 上的响应或GET:

{
    "data": [
        {
            "id": "40",
            "type": "phones",
            "attributes": {
                "number": "(55) 91111.2222"
            },
            "relationships": {
                "contact": {
                    "data": {
                        "id": "1",
                        "type": "contacts"
                    },
                    "links": {
                        "related": "http://localhost:3000/contacts/1"
                    }
                }
            }
        }
    ]
}

希望这个答案