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 υπάρχει ήδη') } 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 }) }, } } })() Συμπερίληψη υπο-πηγών σε ένα REST-ish API - The Codest
The Codest
  • Σχετικά με εμάς
  • Υπηρεσίες
    • Ανάπτυξη λογισμικού
      • Ανάπτυξη Frontend
      • Backend Ανάπτυξη
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Μηχανικοί δεδομένων
      • Μηχανικοί cloud
      • Μηχανικοί QA
      • Άλλα
    • Συμβουλευτική
      • Έλεγχος & Συμβουλευτική
  • Βιομηχανίες
    • Fintech & Τραπεζική
    • E-commerce
    • Adtech
    • Healthtech
    • Κατασκευή
    • Εφοδιαστική
    • Αυτοκίνητο
    • IOT
  • Αξία για
    • CEO
    • CTO
    • Διευθυντής παράδοσης
  • Η ομάδα μας
  • Case Studies
  • Μάθετε πώς
    • Blog
    • Συναντήσεις
    • Διαδικτυακά σεμινάρια
    • Πόροι
Καριέρα Ελάτε σε επαφή
  • Σχετικά με εμάς
  • Υπηρεσίες
    • Ανάπτυξη λογισμικού
      • Ανάπτυξη Frontend
      • Backend Ανάπτυξη
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Μηχανικοί δεδομένων
      • Μηχανικοί cloud
      • Μηχανικοί QA
      • Άλλα
    • Συμβουλευτική
      • Έλεγχος & Συμβουλευτική
  • Αξία για
    • CEO
    • CTO
    • Διευθυντής παράδοσης
  • Η ομάδα μας
  • Case Studies
  • Μάθετε πώς
    • Blog
    • Συναντήσεις
    • Διαδικτυακά σεμινάρια
    • Πόροι
Καριέρα Ελάτε σε επαφή
Πίσω βέλος GO BACK
2022-03-22
Ανάπτυξη λογισμικού

Συμπερίληψη υπο-πηγών σε ένα REST-ish API

The Codest

Krzysztof Buszewicz

Ανώτερος Software Engineer

Θα κατασκευάσουμε μια εφαρμογή βιβλιοθήκης για τη λίστα βιβλίων με (ή χωρίς) δεδομένα συγγραφέων.

Τι θα κάνουμε;

Θα κατασκευάσουμε μια εφαρμογή βιβλιοθήκης για τη λίστα βιβλίων με (ή χωρίς) δεδομένα συγγραφέων. Θα υπάρχει ένα μόνο #index δράση και μερικούς σπόρους. Αυτό θα είναι ένα παράδειγμα εφαρμογής για να δείξουμε πώς μπορείτε να δώσετε σε έναν χρήστη τον έλεγχο των περιεχόμενων υπο-πηγές σε ένα REST-ish API.

"Κριτήρια αποδοχής"

  • Ο χρήστης μπορεί να απαριθμήσει τα βιβλία.
  • Ο χρήστης μπορεί να περάσει περιλαμβάνει παράμετρος ερωτήματος για να φορτώσει τους σχετικούς πόρους (συγγραφέας).
  • περιλαμβάνει Η παράμετρος ερωτήματος έχει μορφή συμβολοσειράς: λέξεις που χωρίζονται με κόμμα και αντιπροσωπεύουν ένθετους πόρους.
  • Θα πρέπει να έχουμε κάποιες σταθερές που να ορίζουν ποιοι πόροι μπορούν να συμπεριληφθούν για ποια ενέργεια.

Εργαλεία

Θα χρησιμοποιήσουμε blueprinter ως σειροποιητή, επειδή είναι ανεξάρτητος από τη μορφή και αρκετά ευέλικτος. Αυτό είναι ένα μοναδικό κόσμημα που θα προσθέσουμε στην τυπική εργαλειοθήκη του rails.

Η εφαρμογή

Ας δημιουργήσουμε ένα παράδειγμα εφαρμογής. Δεν θα προσθέσουμε πλαίσιο δοκιμών, καθώς δεν είναι στο πεδίο εφαρμογής μας.

ράγες νέα βιβλιοθήκη -T

Τώρα δημιουργήστε Συγγραφέας μοντέλο:

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

Και Βιβλίο:

rails g model book συγγραφέας:αναφορές title:string
# => κλήση ενεργού_εγγραφής
# => δημιουργία db/migrate/20211224084614_create_books.rb
# => δημιουργία app/models/book.rb

Θα χρειαστούμε μερικούς σπόρους:

# 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: 'The Three Musketeers')
Book.create(author: lewis, title: 'The Lion, the Witch and the Wardrobe')
Book.create(author: martin, title: 'Clean Code')

Και τώρα είμαστε έτοιμοι να εκτελέσουμε τις μεταναστεύσεις και να σπείρουμε τη βάση δεδομένων:

rails db:migrate && rails db:seed

Ας προσθέσουμε has_many για βιβλία σε Συγγραφέας μοντέλο:

# app/models/author.rb

class Author < ApplicationRecord
  has_many :books
end

Ήρθε η ώρα να γράψουμε έναν ελεγκτή που θα επιστρέφει τα δεδομένα μας. Θα χρησιμοποιήσουμε API namespace, οπότε πρώτα ας προσθέσουμε ένα ακρωνύμιο στις κλίσεις:

# config/initializers/inflections.rb

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

Εντάξει, ας προσθέσουμε τον σειροποιητή μας στο Gemfile:

# Προσθήκη στο Gemfile

gem 'blueprinter'

Και φυσικά να το εγκαταστήσετε:

εγκατάσταση δέσμης

Τότε μπορούμε να φτιάξουμε τα σχέδιά μας:

# app/blueprints/author_blueprint.rb

class AuthorBlueprint < Blueprinter::Base
  αναγνωριστικό :id

  πεδία :name
end
# app/blueprints/book_blueprint.rb

class BookBlueprint < Blueprinter::Base
  αναγνωριστικό :id

  πεδία :title

  association :author, blueprint: AuthorBlueprint
end

Προσθέστε έναν βασικό ελεγκτή για API:

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

ενότητα API
  ενότητα V1
    class BaseController < ActionController::API
    end
  end
end

Και το προσχέδιο της έκδοσής μας BooksController:

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

ενότητα API
  ενότητα V1
    class BooksController < BaseController
      def index
        books = Book.all

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

Πρέπει επίσης να ορίσουμε τη δρομολόγηση φυσικά:

# config/routes.rb

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

Ας δοκιμάσουμε αυτό που έχουμε κάνει μέχρι τώρα:

ράγες 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": "The Lion, the Witch and the Wardrobe"},{"id":3, "author":{"id":3, "name": "Robert C. Martin"}, "title": "Clean Κωδικός"}]

Τα δεδομένα φαίνονται να είναι εντάξει, αλλά τι γίνεται με τα αρχεία καταγραφής;

# αρχεία καταγραφής αιτήσεων (n+1)

Ξεκίνησε το GET "/api/v1/books" για 127.0.0.1 στις 2021-12-24 10:19:40 +0100
Επεξεργασία από API::V1::BooksController#index ως */*
  Φόρτωση βιβλίων (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
  Φόρτωση συγγραφέων (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
  Φόρτωση συγγραφέων (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
  Φόρτωση συγγραφέων (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
Ολοκληρώθηκε 200 OK σε 6ms (Views: 0.1ms | ActiveRecord: 0.4ms | Allocations: 3134)

Με τη χρήση της συσχέτισης στους σειροποιητές μας εισαγάγαμε n+1 πρόβλημα. Θέλουμε να το εξαλείψουμε προσθέτοντας στον χρήστη έναν έλεγχο για το τι ζητάει σε αυτό το τελικό σημείο. Έτσι θα πρέπει να μπορεί είτε να φορτώνει μόνο βιβλία, είτε να περνάει την παράμετρο includes και να παίρνει και συγγραφείς, αλλά κατά προτίμηση χωρίς το n+1.

Ας ορίσουμε μια σταθερά που θα κρατάει μια πληροφορία σχετικά με το ποιες assocs των βιβλίων μπορεί να συμπεριλάβει ο χρήστης στο books#index δράση:

# lib/constants/books/includes.rb

module Constants
  module Βιβλία
    module Περιλαμβάνει
      ALLOWED = {
        index: %i[
          συγγραφέας
        ].freeze
      }.freeze
    τέλος
  end
end

Στη συνέχεια, ορίζουμε έναν χώρο ονομάτων για κενές σταθερές αντικειμένων:

# lib/constants/empty.rb

module Constants
  module Empty
    HASH = {}.freeze
  end
end

Και εδώ είναι η κύρια υπηρεσία μας για την αδειοδότηση περιλαμβάνει. Νομίζω ότι ο κώδικας είναι αρκετά αυτονόητος, μερικά κομμάτια του μαγικό κατανέμονται μόνο σε #default_resources_key και #default_purpose. Αυτές οι μέθοδοι έχουν οριστεί για να μας επιτρέπουν να καλούμε την άδεια περιλαμβάνει περνώντας μόνο params στους ελεγκτές του rails. Η έξοδος θα είναι ο κατακερματισμός που αποθηκεύει true για κάθε επιτρεπόμενη ένταξη.

# 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 unless 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

Τώρα πρέπει να χρησιμοποιήσουμε τα κλειδιά για να φορτώσουμε τα includes και να περάσουμε το ίδιο το hash των inlcudes στον serializer:

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

ενότητα API
  ενότητα 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

Και έτσι πρέπει να προσαρμόσουμε τον σειροποιητή μας - φορτώνουμε τη συσχέτιση μόνο αν περιλαμβάνεται:

# app/blueprints/book_blueprint.rb
class BookBlueprint (_field_name, _book, options) {
                         options[:includes] && options[:includes][:author]
                       }
end

Ας το ξαναδοκιμάσουμε:

ράγες s
curl http://localhost:3000/api/v1/books
# => [{"id":1, "title": "The Three Musketeers"},{"id":2, "title": "The Lion, the Witch and the Wardrobe"},{"id":3, "title": "Clean Code"}]
Ημερολόγια αιτήσεων # (φορτώνουμε μόνο βιβλία)
Ξεκίνησε το GET "/api/v1/books" για ::1 στις 2021-12-24 10:33:41 +0100
Επεξεργασία από το API::V1::BooksController#index ως */*
   (0.1ms) SELECT sqlite_version(*)
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
  Φόρτωση βιβλίων (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
Ολοκληρώθηκε 200 OK σε 9ms (Views: 0.1ms | ActiveRecord: 0.9ms | Allocations: 4548)

Ωραία, δεν έχουμε περάσει τα περιεχόμενα, οπότε έχουμε μόνο βιβλία, χωρίς συγγραφείς. Ας τα ζητήσουμε τώρα:

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"}, "title": "The Lion, the Witch and the Wardrobe"},{"id":3, "author":{"id":3, "name": "Robert C. Martin"}, "title": "Clean Code"}]% 
Αρχεία καταγραφής αιτήσεων # (εξαλείφθηκε n+1)

Ξεκίνησε το GET "/api/v1/books?includes=author" για ::1 στις 2021-12-24 10:38:23 +0100
Επεξεργασία από το API::V1::BooksController#index ως */*
  Παράμετροι: {"includes"=>"author"}
  Φόρτωση βιβλίων (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
  Φόρτωση συγγραφέων (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'
Ολοκληρώθηκε 200 OK σε 17ms (Views: 0.1ms | ActiveRecord: 0.7ms | Allocations: 7373)

Ωραία! Φορτώσαμε την ένωση και εξαλείψαμε n+1 πρόβλημα. Η υπηρεσία μπορεί να χρησιμοποιηθεί για οποιονδήποτε πόρο, το μόνο που θέλουμε να κάνουμε είναι να προσθέσουμε τις επιτρεπόμενες σταθερές στην κατάλληλη μορφή και να τις προσθέσουμε στο PermitIncludes::ALLOWED_INCLUDES.

Πρέπει να θυμόμαστε ότι αυτό θα πρέπει μάλλον να χρησιμοποιείται με σελιδοποίηση (και προσοχή), επειδή η συμπερίληψη συσχετίσεων μπορεί να "φάει" πολλή μνήμη.

Σχετικά άρθρα

Fintech

5 παραδείγματα της καλύτερης χρήσης της Ruby

Αναρωτηθήκατε ποτέ τι μπορούμε να κάνουμε με τη Ruby; Λοιπόν, ο ουρανός είναι μάλλον το όριο, αλλά είμαστε στην ευχάριστη θέση να μιλήσουμε για μερικές περισσότερο ή λιγότερο γνωστές περιπτώσεις...

The Codest
Pawel Muszynski Software Engineer
Ανάπτυξη λογισμικού

Πολυμορφισμός στη Ruby και τη GraphQL

Σε αυτό το άρθρο, θα παρουσιάσω τη χρήση του πολυμορφισμού στην GraphQL. Πριν ξεκινήσω, ωστόσο, αξίζει να υπενθυμίσω τι είναι ο πολυμορφισμός και η GraphQL.

Lukasz Brzeszcz
E-commerce

Διλήμματα ασφάλειας στον κυβερνοχώρο: Διαρροές δεδομένων

Η προ-Χριστουγεννιάτικη βιασύνη βρίσκεται σε πλήρη εξέλιξη. Σε αναζήτηση δώρων για τους αγαπημένους τους, οι άνθρωποι είναι όλο και πιο πρόθυμοι να "μπουκάρουν" στα ηλεκτρονικά καταστήματα

The Codest
Jakub Jakubowicz CTO & Συνιδρυτής
Ανάπτυξη λογισμικού

Μια απλή εφαρμογή Ruby από την αρχή με Active Record

Το MVC είναι ένα μοτίβο σχεδίασης που διαχωρίζει τις αρμοδιότητες μιας εφαρμογής για να διευκολύνει τη μετακίνησή της. Το Rails ακολουθεί αυτό το μοτίβο σχεδίασης κατά σύμβαση.

The Codest
Damian Watroba Software Engineer

Εγγραφείτε στη βάση γνώσεών μας και μείνετε ενήμεροι για την τεχνογνωσία από τον τομέα της πληροφορικής.

    Σχετικά με εμάς

    The Codest - Διεθνής εταιρεία ανάπτυξης λογισμικού με κέντρα τεχνολογίας στην Πολωνία.

    Ηνωμένο Βασίλειο - Έδρα

    • Γραφείο 303B, 182-184 High Street North E6 2JA
      Λονδίνο, Αγγλία

    Πολωνία - Τοπικοί κόμβοι τεχνολογίας

    • Πάρκο γραφείων Fabryczna, Aleja
      Pokoju 18, 31-564 Κρακοβία
    • Πρεσβεία του εγκεφάλου, Konstruktorska
      11, 02-673 Βαρσοβία, Πολωνία

      The Codest

    • Αρχική σελίδα
    • Σχετικά με εμάς
    • Υπηρεσίες
    • Case Studies
    • Μάθετε πώς
    • Καριέρα
    • Λεξικό

      Υπηρεσίες

    • Συμβουλευτική
    • Ανάπτυξη λογισμικού
    • Backend Ανάπτυξη
    • Ανάπτυξη Frontend
    • Staff Augmentation
    • Backend Developers
    • Μηχανικοί cloud
    • Μηχανικοί δεδομένων
    • Άλλα
    • Μηχανικοί QA

      Πόροι

    • Γεγονότα και μύθοι σχετικά με τη συνεργασία με εξωτερικό συνεργάτη ανάπτυξης λογισμικού
    • Από τις ΗΠΑ στην Ευρώπη: Γιατί οι αμερικανικές νεοσύστατες επιχειρήσεις αποφασίζουν να μετεγκατασταθούν στην Ευρώπη
    • Σύγκριση υπεράκτιων κόμβων ανάπτυξης τεχνολογίας: Ευρώπη (Πολωνία), ASEAN (Φιλιππίνες), Ευρασία (Τουρκία)
    • Ποιες είναι οι κορυφαίες προκλήσεις των CTOs και των CIOs;
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Πνευματικά δικαιώματα © 2025 από The Codest. Όλα τα δικαιώματα διατηρούνται.

    elGreek
    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 etEstonian elGreek