window.pipedriveLeadboosterConfig = { base: leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster on juba olemas') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Alamressursside lisamine REST-ish API-s - The Codest
The Codest
  • Meie kohta
  • Teenused
    • Tarkvaraarendus
      • Frontend arendus
      • Backend arendus
    • Staff Augmentation
      • Frontend arendajad
      • Backend arendajad
      • Andmeinsenerid
      • Pilveinsenerid
      • QA insenerid
      • Muud
    • See nõuandev
      • Audit ja nõustamine
  • Tööstusharud
    • Fintech & pangandus
    • E-commerce
    • Adtech
    • Healthtech
    • Tootmine
    • Logistika
    • Autotööstus
    • IOT
  • Väärtus
    • CEO
    • CTO
    • Tarnejuht
  • Meie meeskond
  • Case Studies
  • Tea kuidas
    • Blogi
    • Kohtumised
    • Veebiseminarid
    • Ressursid
Karjäärivõimalused Võtke ühendust
  • Meie kohta
  • Teenused
    • Tarkvaraarendus
      • Frontend arendus
      • Backend arendus
    • Staff Augmentation
      • Frontend arendajad
      • Backend arendajad
      • Andmeinsenerid
      • Pilveinsenerid
      • QA insenerid
      • Muud
    • See nõuandev
      • Audit ja nõustamine
  • Väärtus
    • CEO
    • CTO
    • Tarnejuht
  • Meie meeskond
  • Case Studies
  • Tea kuidas
    • Blogi
    • Kohtumised
    • Veebiseminarid
    • Ressursid
Karjäärivõimalused Võtke ühendust
Tagasi nool TAGASI
2022-03-22
Tarkvaraarendus

Alamressursside lisamine REST-Ša API-sse

The Codest

Krzysztof Buszewicz

Vanem Software Engineer

Me ehitame raamatute riiuli rakenduse, et loetleda raamatuid koos (või ilma) autorite andmetega.

Mida me teeme?

Me ehitame raamatute riiuli rakenduse, et loetleda raamatuid koos (või ilma) autorite andmetega. Seal saab olema üks #index tegevus ja mõned seemned. See on näidisrakendus, mis näitab, kuidas saab anda kasutajale kontrolli lisatud alamressursid REST-ish APIs.

"Vastuvõtukriteeriumid"

  • Kasutaja saab loetleda raamatuid.
  • Kasutaja saab läbida sisaldab päringuparameeter seotud ressursside laadimiseks (autor).
  • sisaldab päringuparameetril on stringi formaat: komadega eraldatud sõnad, mis esindavad üksteise sisseehitatud ressursse.
  • Meil peaks olema mõned konstandid, mis määratlevad, millised ressursid on millise tegevuse puhul kaasatavad.

Tööriistad

Me kasutame blueprinter kui seriaalaja, sest see on formaatidest sõltumatu ja üsna paindlik. See on ainus pärl, mille me lisame rails'i standardse tööriistakomplekti.

Rakendus

Loome näidisrakenduse. Me ei lisa testraamistikku, kuna see ei kuulu meie valdkonda.

rööbasteed uus raamaturiiul -T

Nüüd loo Autor mudel:

rails g mudeli autori nimi:string
#=> invoke active_record
#=> create db/migrate/20211224084524_create_authors.rb
#=> create app/models/author.rb

Ja Raamat:

rööbasteed g mudel raamat autor:viited pealkiri:string
# => invoke active_record
# => create db/migrate/20211224084614_create_books.rb
# => luua app/models/book.rb

Me vajame seemneid:

# db/seeds.rb

dumas = Author.create(name: 'Alexandre Dumas')
lewis = Author.create(name: 'C.S. Lewis')
martin = Author.create(name: 'Robert C. Martin')

Book.create(author: dumas, title: 'Kolm musketäri')
Book.create(author: lewis, title: "The Lion, the Witch and the Wardrobe")
Book.create(author: martin, title: 'Clean Code')

Ja nüüd oleme valmis migratsiooni käivitamiseks ja andmebaasi külvamiseks:

rails db:migrate && rails db:seed

Lisame has_many raamatute jaoks Autor mudel:

# app/models/author.rb

class Autor < ApplicationRecord
  has_many :books
end

On aeg kirjutada kontroller, mis tagastab meie andmed. Me kasutame API nimeruumi, seega lisame kõigepealt akronüümi inflections:

# config/initializers/initializers/inflections.rb

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym 'API'
end

Okei, lisame meie serializer'i, et Gemfile:

# Lisa Gemfile'ile

gem 'blueprinter'

Ja muidugi paigaldage see:

komplekti paigaldamine

Siis saame ehitada oma plaanid:

# app/blueprints/author_blueprint.rb

class AuthorBlueprint < Blueprinter::Base
  identifier :id

  väljad :name
end
# app/blueprints/book_blueprint.rb

class BookBlueprint < Blueprinter::Base
  identifier :id

  väljad :title

  association :author, blueprint: AuthorBlueprint
end

Lisage baaskontroller API:

# app/controllers/api/v1/base_controller.rb

moodul API
  moodul V1
    class BaseController < ActionController::API
    end
  end
end

Ja meie eelnõu versioon BooksController:

# app/controllers/api/v1/books_controller.rb

moodul API
  moodul V1
    class BooksController < BaseController
      def index
        books = Book.all

        render json: BookBlueprint.render(books)
      end
    end
  end
end

Loomulikult peame määratlema ka marsruutimise:

# config/routes.rb

Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :books, only: :index
    end
  end
end

Katsetame, mida oleme seni teinud:

rööpad s 
curl http://localhost:3000/api/v1/books

# => [{"id":1, "author":{"id":1, "name": "Alexandre Dumas"}, "title": "The Three Musketeers"},{"id":2, "author":{"id":2, "name": "C.S. Lewis"}, "title": "Lõvi, nõid ja riidekapp"},{"id":3, "author":{"id":3, "name": "Robert C. Martin"}, "title": "Clean Kood"}]

Andmed tunduvad olevat korras, aga kuidas on lood logidega?

# taotluse logid (n+1)

Käivitati GET "/api/v1/books" 127.0.0.1 jaoks kell 2021-12-24 10:19:40 +0100
Töötlemine API::V1::BooksController#index poolt nagu */*
  Raamatu laadimine (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
  Autorite laadimine (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
  Autorite laadimine (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
  Autorite laadimine (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
Lõpetatud 200 OK 6ms jooksul (Views: 0.1ms | ActiveRecord: 0.4ms | Allocations: 3134)

Kasutades assotsiatsiooni meie seriaatorites, võtsime kasutusele n+1 probleem. Me tahame selle kõrvaldada, lisades kasutajale kontrolli selle üle, mida ta selles lõpp-punktis taotleb. Nii et ta peaks saama kas laadida ainult raamatuid või anda üle parameeter includes ja saada ka autorid, kuid soovitavalt ilma n+1.

Määratleme konstandi, mis hoiab teavet selle kohta, milliseid raamatute assocs kasutaja saab lisada books#index tegevus:

# lib/constants/books/includes.rb

moodul Constants
  moodul Books
    moodul Includes
      ALLOWED = {
        indeks: %i[
          autor
        ].freeze
      }.freeze
    end
  end
end

Järgmisena määratleme nimeruumi tühjade objektikonstantide jaoks:

# lib/constants/empty.rb

moodul Konstandid
  moodul Tühi
    HASH = {}.freeze
  end
end

Ja siin on meie peamine teenus lubade andmiseks sisaldab. Ma arvan, et kood on üsna iseenesestmõistetav, mõned tükid magic eraldatakse ainult #default_resources_key ja #default_purpose. Need meetodid on defineeritud selleks, et võimaldada meil kutsuda permit include'i ainult parameetrite edastamisega rails'i kontrollerites. Väljundiks on hash, mis salvestab tõsi iga lubatud kaasamise puhul.

# app/services/permit_includes.rb

require 'constants/empty'
require 'constants/books/includes'

class PermitIncludes
  Empty = Constants::Empty

  COMMA = ','
  SLASH = '/'

  INCLUDES_FORMAT = /A[a-z]+(,[a-z]+)*z/.freeze
  ALLOWED_INCLUDES = {
    books: Constants::Books::Includes::ALLOWED
  }.freeze

  def call(params, resources: default_resources_key(params), purpose: default_purpose(params))
    return Empty::HASH, välja arvatud juhul, kui includes_sent?(params)
    return Empty::HASH unless includes_valid?(params)

    requested_includes = parse_includes(params)
    allowed_includes = filter_includes(requested_includes, resources, purpose)

    allowed_includes.index_with(true)
  end

  private

  def default_resources_key(params)
    raise(ArgumentError, 'params :controller key must be a string') unless params[:controller].is_a?(String)

    params[:controller].split(SLASH).last&.to_sym
  end

  def default_purpose(params)
    raise(ArgumentError, 'params :action key must be a string') unless params[:action].is_a?(String)

    params[:action].to_sym
  end

  def includes_sent?(params)
    params.key?(:includes)
  end

  def includes_valid?(params)
    return false unless params[:includes].is_a?(String)

    params[:includes].match?(INCLUDES_FORMAT)
  end

  def parse_includes(params)
    params[:includes].split(COMMA).map(&:to_sym)
  end

  def filter_includes(requested_includes, resources_key, purpose)
    requested_includes & ALLOWED_INCLUDES[resources_key][purpose]
  end
end

Nüüd peame kasutama võtmeid, et laadida include'id ja edastada inlcudes hash ise serializerile:

# app/controllers/api/v1/books_controller.rb

moodul API
  moodul V1
    class BooksController < BaseController
      def index
        includes = PermitIncludes.new.call(params)
        books = Book.includes(includes.keys).all

        render json: BookBlueprint.render(books, includes: includes)
      end
    end
  end
end

Ja see on see, kuidas me peame oma serializer'i sättima - me laadime assotsiatsiooni ainult siis, kui see sisaldub:

# app/blueprints/book_blueprint.rb
class BookBlueprint (_väli_nimi, _raamat, valikud) {
                         options[:includes] && options[:includes][:author]
                       }
end

Katsetame seda uuesti:

rööpad s
curl http://localhost:3000/api/v1/books
# => [{"id":1, "title": "Kolm musketäri"},{"id":2, "title": "Lõvi, nõid ja riidekapp"},{"id":3, "title": "Puhas kood"}]
# taotluse logid (me laadime ainult raamatuid)
Käivitati GET "/api/v1/books" jaoks ::1 kell 2021-12-24 10:33:41 +0100
Töötlemine API::V1::BooksController#indeksina */*
   (0.1ms) SELECT sqlite_version(*)
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
  Raamatu laadimine (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Lõpetatud 200 OK 9ms jooksul (Views: 0.1ms | ActiveRecord: 0.9ms | Allocations: 4548)

Hea, me ei ole läbinud sisaldab nii sai ainult raamatuid, ilma autorid. Taotleme nüüd neid:

curl 'http://localhost:3000/api/v1/books?includes=author'
# => [{"id":1, "author":{"id":1, "name": "Alexandre Dumas"}, "title": "The Three Musketeers"},{"id":2, "author":{"id":2, "name": "C.S. Lewis"}, "pealkiri": "Lõvi, nõid ja riidekapp"},{"id":3, "autor":{"id":3, "nimi": "Robert C. Martin"}, "pealkiri": "Puhas kood"}]% 
# taotluse logid (kõrvaldatud n+1)

Käivitati GET "/api/v1/books?includes=author" jaoks ::1 kell 2021-12-24 10:38:23 +0100
Töötlemine API::V1::BooksController#indeksina */*
  Parameetrid: {"includes"=>"author"}
  Raamatu laadimine (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
  Autorite laadimine (0.2ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" IN (?, ?, ?) [["id", 1], ["id", 2], ["id", 3]]
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Lõpetatud 200 OK 17ms jooksul (Views: 0.1ms | ActiveRecord: 0.7ms | Allocations: 7373)

Lahe! Me saime assotsiatsiooni koormatud ja kõrvaldatud n+1 probleem. Teenust saab kasutada mis tahes ressursi jaoks, kõik, mida me tahame teha, on lisada lubatud inlcudes konstandid õiges formaadis ja lisada need aadressile PermitIncludes::ALLOWED_INCLUDES.

Me peame meeles pidama, et seda tuleks ilmselt kasutada koos leheküljeotsinguga (ja ettevaatlikult), sest assotsiatsioonide lisamine võib "süüa" palju mälu.

Seotud artiklid

Fintech

5 näidet Ruby parimast kasutamisest

Kas olete kunagi mõelnud, mida me saame teha Ruby'ga? Noh, taevas on ilmselt piirideta, kuid me räägime hea meelega mõnest rohkem või vähem teadaolevast juhtumist...

The Codest
Pawel Muszynski Software Engineer
Tarkvaraarendus

Polümorfism Ruby's ja GraphQLis

Selles artiklis tutvustan polümorfismi kasutamist GraphQLis. Enne alustamist tasub aga meenutada, mis on polümorfism ja GraphQL.

Lukasz Brzeszcz
E-commerce

Küberturbe dilemmad: Andmete lekkimine

Jõulueelne kiire on täies hoos. Oma lähedastele kingitusi otsides on inimesed üha enam valmis "tormama" veebipoodidesse.

The Codest
Jakub Jakubowicz CTO & kaasasutajad
Tarkvaraarendus

Lihtne Ruby rakendus algusest peale koos Active Recordiga

MVC on disainimudel, mis jagab rakenduse vastutusalad, et muuta liikumine lihtsamaks. Rails järgib seda disainimustrit kokkuleppeliselt.

The Codest
Damian Watroba Software Engineer

Tellige meie teadmistebaas ja jääge kursis IT-sektori eksperditeadmistega.

    Meie kohta

    The Codest - rahvusvaheline tarkvaraarendusettevõte, mille tehnoloogiakeskused asuvad Poolas.

    Ühendkuningriik - peakorter

    • Büroo 303B, 182-184 High Street North E6 2JA
      London, Inglismaa

    Poola - kohalikud tehnoloogiakeskused

    • Fabryczna büroopark, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Varssavi, Poola

      The Codest

    • Kodu
    • Meie kohta
    • Teenused
    • Case Studies
    • Tea kuidas
    • Karjäärivõimalused
    • Sõnastik

      Teenused

    • See nõuandev
    • Tarkvaraarendus
    • Backend arendus
    • Frontend arendus
    • Staff Augmentation
    • Backend arendajad
    • Pilveinsenerid
    • Andmeinsenerid
    • Muud
    • QA insenerid

      Ressursid

    • Faktid ja müüdid koostööst välise tarkvaraarenduspartneriga
    • USAst Euroopasse: Miks otsustavad Ameerika idufirmad Euroopasse ümber asuda?
    • Tech Offshore arenduskeskuste võrdlus: Euroopa (Poola), ASEAN (Filipiinid), Euraasia (Türgi).
    • Millised on CTO ja CIOde peamised väljakutsed?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Copyright © 2025 by The Codest. Kõik õigused kaitstud.

    etEstonian
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch elGreek etEstonian