Middleware ของ Rack คืออะไร


267

Middleware ของ Rack ใน Ruby คืออะไร? ฉันไม่สามารถหาคำอธิบายที่ดีสำหรับสิ่งที่พวกเขาหมายถึงโดย "มิดเดิลแวร์"


4
นอกจากนี้ยังมีคำแนะนำเกี่ยวกับ RailsGuide ที่ครอบคลุม Rack อย่างครอบคลุมรวมถึงมิดเดิลแวร์: guide.rubyonrails.org/rails_on_rack.html
xji

ขอบคุณมากกับทีม PhusionPassenger พวกเขามีบทความที่อธิบายไว้อย่างดีในบล็อกของพวกเขา rubyraptor.org/…
Lamian

Rack และชั้นตัวกลางมีการอธิบายในนี้บทความ อธิบายเกี่ยวกับการสร้างแอปพลิเคชันที่ใช้แร็คด้วย
shashwat srivastava

คำตอบ:


353

ชั้นวางตามแบบ

Rack middleware เป็นมากกว่า "วิธีกรองคำขอและการตอบกลับ" - เป็นการใช้รูปแบบการออกแบบไปป์ไลน์สำหรับเว็บเซิร์ฟเวอร์ที่ใช้Rackแร็ค

มันแยกขั้นตอนการประมวลผลคำขอออกจากกันอย่างหมดจดโดยแยกข้อกังวลออกเป็นเป้าหมายหลักของผลิตภัณฑ์ซอฟต์แวร์ที่ออกแบบมาอย่างดี

ตัวอย่างเช่นด้วย Rack ฉันสามารถแยกขั้นตอนการทำงานของไปป์ไลน์ได้:

  • การรับรองความถูกต้อง : เมื่อคำขอมาถึงรายละเอียดการเข้าสู่ระบบของผู้ใช้ถูกต้องหรือไม่ ฉันจะตรวจสอบ OAuth นี้การตรวจสอบสิทธิ์พื้นฐาน HTTP ชื่อ / รหัสผ่านได้อย่างไร

  • การอนุญาต : "เป็นผู้ใช้ที่ได้รับอนุญาตให้ทำงานนี้หรือไม่" เช่นความปลอดภัยตามบทบาท

  • การแคช : ฉันได้ดำเนินการตามคำขอนี้ไปแล้วฉันจะคืนผลลัพธ์ที่แคชได้หรือไม่?

  • การตกแต่ง : ฉันจะปรับปรุงการร้องขอเพื่อให้การประมวลผลดาวน์สตรีมดีขึ้นได้อย่างไร

  • การตรวจสอบประสิทธิภาพและการใช้งาน : ฉันจะได้รับสถิติอะไรจากคำขอและการตอบกลับ

  • การดำเนินการ : จัดการคำขอจริงและให้การตอบสนอง

ความสามารถในการแยกขั้นตอนที่แตกต่างกัน (และรวมไว้ด้วยตนเอง) เป็นความช่วยเหลือที่ดีในการพัฒนาแอปพลิเคชันที่มีโครงสร้าง

ชุมชน

นอกจากนี้ยังมีระบบนิเวศที่ยอดเยี่ยมในการพัฒนา Rack Middleware - คุณควรจะสามารถหาส่วนประกอบของแร็คที่สร้างไว้ล่วงหน้าเพื่อทำตามขั้นตอนทั้งหมดข้างต้นและอื่น ๆ ดูแร็ค GitHub วิกิพีเดียสำหรับรายชื่อของตัวกลาง

Middleware คืออะไร

Middleware เป็นคำที่น่ากลัวซึ่งหมายถึงส่วนประกอบซอฟต์แวร์ / ไลบรารีที่ช่วยเหลือด้วย แต่ไม่เกี่ยวข้องโดยตรงในการทำงานบางอย่าง ตัวอย่างที่พบบ่อยมากมีการเข้าสู่ระบบการตรวจสอบและอื่น ๆทั่วไปส่วนประกอบการประมวลผลในแนวนอน สิ่งเหล่านี้มีแนวโน้มที่จะเป็นสิ่งที่ทุกคนต้องการในแอพพลิเคชั่นที่หลากหลาย แต่มีคนจำนวนไม่มากที่สนใจ (หรือควร) ในการสร้างตัวเอง

ข้อมูลมากกว่านี้


สิ่งหนึ่งที่ฉันไม่ชัดเจน: มิดเดิลแวร์ทั้งหมดแชร์ข้อมูลเดียวกันหรือไม่ เป็นไปได้ไหมที่จะแยกพวกมัน (เช่น sandbox หนึ่ง) เพื่อความปลอดภัย?
Brian Armstrong

2
Rack เป็นส่วนหนึ่งของแอปพลิเคชันของคุณดังนั้นมิดเดิลแวร์ทั้งหมดจะทำสำเนาคำขอเดียวกันและแต่ละอันสามารถแก้ไขได้ตามที่พวกเขาต้องการ AFAIK ไม่มีทางที่จะแซนด์บ็อกซ์พวกมันในลักษณะเดียวกันไม่มีทางที่จะแซนด์บ็อกซ์หนึ่งจากอีกวัตถุหนึ่งในกระบวนการเดียวกัน
Chris McCauley

1
และเข้าใจว่า Rack ต่างจาก Rake หรือไม่
Manish Shrivastava

1
ฉันชอบคิดว่ามิดเดิลแวร์เป็นสิ่งที่ตั้งอยู่ตรงกลางแอพของฉันระหว่างสิ่งที่ฉันเขียนและสิ่งที่จะไปและกลับจากเซิร์ฟเวอร์ของฉัน ... ซึ่งโฮสต์อยู่บน rackspace เหตุผลที่คำว่า 'มิดเดิลแวร์ของชั้นวาง' สร้างความสับสนดังที่เราทุกคนทราบกันดีว่าเป็นเพราะขงจื๊อที่เขียนมิดเดิลแวร์ของชั้นวางดั้งเดิมทั้งหมดเมื่อ 2,000 กว่าปีที่แล้ว ในประเทศฝรั่งเศส.
LpLrich

74

ประการแรก Rack เป็นสองสิ่ง:

  • การประชุมส่วนต่อประสานเว็บเซิร์ฟเวอร์
  • อัญมณี

ชั้นวาง - ส่วนต่อประสานเว็บเซิร์ฟเวอร์

พื้นฐานมากของแร็คคือการประชุมที่เรียบง่าย เว็บเซิร์ฟเวอร์ที่สอดคล้องกับชั้นวางทุกคนจะเรียกวิธีการโทรบนวัตถุที่คุณให้เขาและแสดงผลลัพธ์ของวิธีการนั้นเสมอ ชั้นวางระบุวิธีการเรียกวิธีการนี้ให้มีลักษณะและสิ่งที่จะต้องกลับ นั่นคือชั้นวาง

ลองทำดูง่ายๆ ฉันจะใช้ WEBrick เป็นเว็บเซิร์ฟเวอร์ที่สอดคล้องกับชั้นวาง แต่อย่างใดอย่างหนึ่งจะทำ มาสร้างเว็บแอปพลิเคชั่นง่ายๆที่ส่งคืนสตริง JSON สำหรับสิ่งนี้เราจะสร้างไฟล์ชื่อ config.ru config.ru จะถูกเรียกโดยอัตโนมัติโดยคำสั่ง rackup ของ rackup ซึ่งจะเรียกใช้เนื้อหาของ config.ru ในเว็บเซิร์ฟเวอร์ที่สอดคล้องกับชั้นวาง ดังนั้นให้เพิ่มสิ่งต่อไปนี้ในไฟล์ config.ru:

class JSONServer
  def call(env)
    [200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
  end
end

map '/hello.json' do
  run JSONServer.new
end

เนื่องจากการประชุมระบุเซิร์ฟเวอร์ของเรามีวิธีที่เรียกว่าการเรียกที่ยอมรับการแฮ็สภาพแวดล้อมและส่งกลับอาร์เรย์ที่มีรูปแบบ [สถานะส่วนหัวร่างกาย] สำหรับเว็บเซิร์ฟเวอร์ที่จะให้บริการ ลองทำโดยการเรียกแร็ค เซิร์ฟเวอร์ที่รองรับแร็คเริ่มต้นบางที WEBrick หรือ Mongrel จะเริ่มต้นและรอคำขอที่จะให้บริการทันที

$ rackup
[2012-02-19 22:39:26] INFO  WEBrick 1.3.1
[2012-02-19 22:39:26] INFO  ruby 1.9.3 (2012-01-17) [x86_64-darwin11.2.0]
[2012-02-19 22:39:26] INFO  WEBrick::HTTPServer#start: pid=16121 port=9292

ลองทดสอบเซิร์ฟเวอร์ JSON ใหม่ของเราโดยการดัดผมหรือไปที่ url http://localhost:9292/hello.jsonและ voila:

$ curl http://localhost:9292/hello.json
{ message: "Hello!" }

มันได้ผล. ที่ดี! นั่นเป็นพื้นฐานสำหรับทุก ๆ เว็บเฟรมเวิร์กไม่ว่าจะเป็น Rails หรือ Sinatra เมื่อถึงจุดหนึ่งพวกเขาใช้วิธีการโทรทำงานผ่านรหัสกรอบทั้งหมดและในที่สุดก็กลับมาตอบสนองในรูปแบบ [สถานะส่วนหัวร่างกาย]

ใน Ruby on Rails ตัวอย่างเช่น rack ร้องขอจำนวนActionDispatch::Routing.Mapperคลาสที่มีลักษณะดังนี้:

module ActionDispatch
  module Routing
    class Mapper
      ...
      def initialize(app, constraints, request)
        @app, @constraints, @request = app, constraints, request
      end

      def matches?(env)
        req = @request.new(env)
        ...
        return true
      end

      def call(env)
        matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
      end
      ...
  end
end

ดังนั้นโดยทั่วไปการตรวจสอบ Rails ขึ้นอยู่กับการแฮช env หากเส้นทางใดตรงกัน ถ้ามันผ่านการแฮ็ค env ไปยังแอปพลิเคชันเพื่อคำนวณการตอบสนองมิฉะนั้นจะตอบกลับด้วย 404 ทันทีดังนั้นเว็บเซิร์ฟเวอร์ใด ๆ ที่เข้ากันได้กับการประชุมส่วนต่อประสานชั้นวางสามารถให้บริการแอปพลิเคชั่น

มิดเดิ้ล

ชั้นวางยังรองรับการสร้างเลเยอร์มิดเดิลแวร์ พวกเขาโดยทั่วไปจะดักคำขอทำอะไรกับมันและส่งต่อ สิ่งนี้มีประโยชน์มากสำหรับงานอเนกประสงค์

สมมติว่าเราต้องการเพิ่มการบันทึกไปยังเซิร์ฟเวอร์ JSON ของเราที่วัดระยะเวลาที่คำขอใช้ เราสามารถสร้างตัวบันทึกมิดเดิลแวร์ที่ทำสิ่งนี้:

class RackLogger
  def initialize(app)
    @app = app
  end

  def call(env)
    @start = Time.now
    @status, @headers, @body = @app.call(env)
    @duration = ((Time.now - @start).to_f * 1000).round(2)

    puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
    [@status, @headers, @body]
  end
end

เมื่อสร้างขึ้นมันจะบันทึกสำเนาของแอ็พพลิเคชันแร็คจริง ในกรณีของเรานั่นเป็นตัวอย่างของ JSONServer ของเรา Rack จะเรียกวิธีการโทรบนมิดเดิลแวร์โดยอัตโนมัติและคาดว่าจะกลับมา[status, headers, body]อาเรย์เช่นเดียวกับที่ JSONServer ของเราส่งคืน

ดังนั้นในมิดเดิลแวร์นี้จุดเริ่มต้นจะถูกนำมาใช้จากนั้นจะทำการโทรไปยัง JSONServer จริง@app.call(env)จากนั้นตัวบันทึกจะแสดงรายการบันทึกและส่งกลับการตอบสนองในที่สุด[@status, @headers, @body]ที่สุด

ในการทำให้ rackup.ru ของเราใช้มิดเดิลแวร์นี้ให้เพิ่มการใช้ RackLogger ให้เป็นดังนี้:

class JSONServer
  def call(env)
    [200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
  end
end

class RackLogger
  def initialize(app)
    @app = app
  end

  def call(env)
    @start = Time.now
    @status, @headers, @body = @app.call(env)
    @duration = ((Time.now - @start).to_f * 1000).round(2)

    puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
    [@status, @headers, @body]
  end
end

use RackLogger

map '/hello.json' do
  run JSONServer.new
end   

รีสตาร์ทเซิร์ฟเวอร์และ voila มันส่งผลให้เข้าสู่ระบบทุกคำขอ ชั้นวางช่วยให้คุณเพิ่มมิดเดิ้ลแวร์หลายตัวที่ถูกเรียกตามลำดับที่เพิ่มเข้ามา มันเป็นวิธีที่ยอดเยี่ยมในการเพิ่มฟังก์ชันการทำงานโดยไม่ต้องเปลี่ยนแกนหลักของแอปพลิเคชันชั้นวาง

ชั้นวาง - อัญมณี

แม้ว่าชั้นวาง - ก่อนอื่น - เป็นแบบแผน แต่ก็เป็นอัญมณีที่ให้การทำงานที่ยอดเยี่ยม หนึ่งในนั้นเราใช้สำหรับเซิร์ฟเวอร์ JSON ของเราคือคำสั่ง rackup แต่มีอีกมากมาย! rack gem ให้แอปพลิเคชั่นเล็ก ๆ น้อย ๆ สำหรับกรณีการใช้งานจำนวนมากเช่นการให้บริการไฟล์แบบคงที่หรือแม้แต่ไดเรกทอรีทั้งหมด เรามาดูกันว่าเราให้บริการไฟล์ง่าย ๆ อย่างไรเช่นไฟล์ HTML พื้นฐานที่อยู่ใน htmls / index.html:

<!DOCTYPE HTML>
  <html>
  <head>
    <title>The Index</title>
  </head>

  <body>
    <p>Index Page</p>
  </body>
</html>

เราอาจต้องการแสดงไฟล์นี้จากรูทเว็บไซต์ดังนั้นให้เพิ่มสิ่งต่อไปนี้ใน config.ru ของเรา:

map '/' do
  run Rack::File.new "htmls/index.html"
end

หากเราเข้าชมhttp://localhost:9292เราจะเห็นไฟล์ html ของเราแสดงผลอย่างสมบูรณ์ นั่นเป็นเรื่องง่ายใช่มั้ย

มาเพิ่มไดเรกทอรีทั้งหมดของไฟล์จาวาสคริปต์โดยสร้างไฟล์จาวาสคริปต์ใน / javascripts และเพิ่มสิ่งต่อไปนี้ใน config.ru:

map '/javascripts' do
  run Rack::Directory.new "javascripts"
end

รีสตาร์ทเซิร์ฟเวอร์แล้วไปที่http://localhost:9292/javascriptและคุณจะเห็นรายการไฟล์จาวาสคริปต์ทั้งหมดที่คุณสามารถรวมได้ทันทีจากที่ใดก็ได้


3
แต่ไม่ใช่ Rack middleware
รูปี

1
หากคุณไม่ทราบว่าแร็คคืออะไรคุณจะรู้ว่ามันคืออะไรและใช้งานอย่างไรหลังจากที่คุณอ่านโพสต์บล็อกนี้ ดีมาก. ถึงกระนั้นก็ตามการเชื่อมโยงไปยังเอกสารชั้นวางอย่างเป็นทางการในตอนท้ายของโพสต์ไม่สามารถใช้ได้อีกต่อไป!
โคลิน

คุณขวาขอบคุณ ฉันรวมเนื้อหาไว้ในโพสต์และลบลิงก์ที่ไม่ทำงานออก
Thomas Fankhauser

ฉันจะบอกว่ามันไม่ใช่แบบแผน มันเป็นอินเทอร์เฟซสัญญาที่กำหนดไว้อย่างดีสำหรับรูปแบบการตอบสนองคำขอ
Ron Klein

20

ฉันมีปัญหาในการทำความเข้าใจแร็คตัวเองในเวลาที่เหมาะสม ฉันเข้าใจอย่างถ่องแท้แล้วหลังจากที่ทำเว็บเซิร์ฟเวอร์ Ruby ขนาดเล็กนี้ขึ้นมา ฉันได้แบ่งปันการเรียนรู้ของฉันเกี่ยวกับ Rack (ในรูปแบบของเรื่องราว) ที่นี่ในบล็อกของฉัน: http://gauravchande.com/what-is-rack-in-ruby-rails

ข้อเสนอแนะเป็นมากกว่าการต้อนรับ


13
คำตอบสำหรับลิงก์อย่างเดียวนั้นไม่ควรนำมาใช้กับ Stack Overflowเนื่องจากหากทรัพยากรที่ลิงก์ไปใช้งานไม่ได้ในอนาคตคำตอบจะไร้ประโยชน์ อย่างน้อยโปรดสรุปประเด็นที่เกี่ยวข้องของการโพสต์บล็อกของคุณและเพิ่มลงในคำตอบนี้

ขอบคุณสำหรับการโพสต์ ฉันเป็นโปรแกรมเมอร์ Rails ที่เริ่มต้นมากและฉันเข้าใจแนวคิดของ Rack ด้วยโพสต์ที่ชัดเจนของคุณ
Eduardo Ramos

โพสต์บล็อกที่ยอดเยี่ยม คำตอบอื่น ๆ ดูเหมือนจะค่อนข้างซับซ้อน IMO
หอย

ช่างเป็นคำอธิบายที่ดีมาก ขอบคุณ Gaurav
rovitulli

7

config.ru ตัวอย่าง runnable น้อยที่สุด

app = Proc.new do |env|
  [
    200,
    {
      'Content-Type' => 'text/plain'
    },
    ["main\n"]
  ]
end

class Middleware
  def initialize(app)
    @app = app
  end

  def call(env)
    @status, @headers, @body = @app.call(env)
    [@status, @headers, @body << "Middleware\n"]
  end
end

use(Middleware)

run(app)

เรียกใช้และการเยี่ยมชมrackup localhost:9292ผลลัพธ์คือ:

main
Middleware

ดังนั้นจึงเป็นที่ชัดเจนว่าMiddlewarewraps และเรียกแอปหลัก ดังนั้นจึงสามารถดำเนินการตามคำขอล่วงหน้าได้และดำเนินการตอบกลับด้วยวิธีใดก็ได้

ตามที่อธิบายไว้ที่: http://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack , Rails ใช้ Middlewares ของ Rack สำหรับฟังก์ชั่นมากมายและคุณสามารถเพิ่มคุณเองด้วยconfig.middleware.useวิธีการแบบครอบครัว

ข้อได้เปรียบของการใช้งานฟังก์ชั่นในมิดเดิลแวร์คือคุณสามารถนำมันกลับมาใช้ใหม่บนเฟรมเวิร์กแร็คใด ๆ ก็ได้ดังนั้นรูบีหลักที่สำคัญทั้งหมดไม่ใช่แค่ Rails


6

Rack middleware เป็นวิธีการกรองคำขอและการตอบสนองที่เข้ามาในแอปพลิเคชันของคุณ คอมโพเนนต์มิดเดิลแวร์อยู่ระหว่างไคลเอนต์และเซิร์ฟเวอร์ประมวลผลคำขอขาเข้าและตอบกลับขาออก แต่เป็นมากกว่าส่วนติดต่อที่สามารถใช้เพื่อพูดคุยกับเว็บเซิร์ฟเวอร์ มันถูกใช้ในการจัดกลุ่มและโมดูลการสั่งซื้อซึ่งมักจะเป็นคลาสทับทิมและระบุการพึ่งพาระหว่างพวกเขา โมดูลมิดเดิลแวร์ของชั้นวางเท่านั้น: - มีตัวสร้างที่ใช้แอปพลิเคชันถัดไปในสแต็กเป็นพารามิเตอร์ - ตอบสนองต่อวิธีการ "เรียก" ที่ใช้แฮสภาพแวดล้อมเป็นพารามิเตอร์ ค่าที่ส่งคืนจากการโทรนี้คืออาร์เรย์ของ: รหัสสถานะแฮชของสภาพแวดล้อมและเนื้อหาการตอบกลับ


4

ฉันใช้ Middleware ของ Rack เพื่อแก้ปัญหาสองข้อ:

  1. การตรวจสอบข้อผิดพลาดในการแยกวิเคราะห์ JSON ด้วยมิดเดิลแวร์ของ Rack ที่กำหนดเองและส่งคืนข้อความแสดงข้อผิดพลาดที่จัดรูปแบบอย่างสวยงามเมื่อไคลเอ็นต์ส่ง JSON ที่ถูกจับ
  2. การบีบอัดเนื้อหาผ่าน Rack :: Deflater

มันจ่ายการแก้ไขที่หรูหราสวยในทั้งสองกรณี


2
คำตอบนี้ในขณะที่ค่อนข้างมีประโยชน์ไม่ได้ตอบคำถามของRack Middlewareว่าจริง ๆ แล้ว

นอกจากนี้คำตอบนี้เป็นคำตอบที่ค่อนข้างลิงก์ ... : P
Smar

4

ชั้นวางคืออะไร

Rack จัดเตรียมอินเตอร์เฟสที่น้อยที่สุดระหว่างเว็บเซิร์ฟเวอร์ที่สนับสนุนเฟรมเวิร์ก Ruby และ Ruby

การใช้ชั้นวางคุณสามารถเขียนแอปพลิเคชันชั้นวางได้

ชั้นวางจะผ่านการแฮชของสภาพแวดล้อม (แฮชที่อยู่ภายในคำขอ HTTP จากไคลเอนต์ซึ่งประกอบด้วยหัวคล้าย CGI) ไปยังแอปพลิเคชันชั้นวางของคุณซึ่งสามารถใช้สิ่งต่าง ๆ ที่อยู่ในแฮชนี้เพื่อทำสิ่งที่มันต้องการ

แอปพลิเคชัน Rack คืออะไร

ในการใช้ Rack คุณต้องระบุ 'แอพ' - วัตถุที่ตอบสนองต่อ#callวิธีการที่มีแฮชของสภาพแวดล้อมเป็นพารามิเตอร์ (โดยทั่วไปจะกำหนดเป็นenv) #callต้องส่งคืน Array ที่มีสามค่าอย่างแน่นอน:

  • รหัสสถานะ (เช่น '200')
  • Hash ของส่วนหัว ,
  • การตอบสนองของร่างกาย (ซึ่งต้องตอบสนองต่อวิธีทับทิมeach)

คุณสามารถเขียนแอปพลิเคชันชั้นวางที่ส่งกลับอาร์เรย์ดังกล่าว - ซึ่งจะถูกส่งกลับไปยังไคลเอนต์ของคุณโดยชั้นวางในการตอบกลับ (ซึ่งจริงๆแล้วจะเป็นตัวอย่างของ Class Rack::Response[คลิกเพื่อไปที่เอกสาร])

แอปพลิเคชันแร็คที่ง่ายมาก:

  • gem install rack
  • สร้างconfig.ruไฟล์ - Rack รู้ที่จะมองหาสิ่งนี้

เราจะสร้างแอปพลิเคชัน Rack ขนาดเล็กที่ส่งกลับการตอบสนอง (ตัวอย่างของRack::Response) Who's Response Body เป็นอาร์เรย์ที่มีสตริง:"Hello, World!"ที่ตอบสนองของร่างกายคืออาร์เรย์ที่มีสตริง:

เราจะเปิดไฟเซิร์ฟเวอร์ภายในโดยใช้คำสั่ง rackupเราจะยิงขึ้นเซิร์ฟเวอร์ท้องถิ่นโดยใช้คำสั่ง

เมื่อไปที่พอร์ตที่เกี่ยวข้องในเบราว์เซอร์ของเราเราจะเห็น "สวัสดีโลก!" เรนเดอร์ในวิวพอร์ต

#./message_app.rb
class MessageApp
  def call(env)
    [200, {}, ['Hello, World!']]
  end
end

#./config.ru
require_relative './message_app'

run MessageApp.new

เปิดไฟเซิร์ฟเวอร์ภายในด้วยrackupและเยี่ยมชมlocalhost: 9292และคุณควรเห็น 'Hello, World!' การแสดงผล

นี่ไม่ใช่คำอธิบายที่ครอบคลุม แต่ที่สำคัญสิ่งที่เกิดขึ้นที่นี่คือไคลเอนต์ (เบราว์เซอร์) ส่งคำร้องขอ HTTP ไปยัง Rack ผ่านเซิร์ฟเวอร์ภายในเครื่องของคุณและ Rack MessageAppจะเริ่มต้นและเรียกใช้callโดยส่งผ่าน Hash ของสภาพแวดล้อมเป็นพารามิเตอร์envอาร์กิวเมนต์)

Rack ใช้ค่าส่งคืน (อาร์เรย์) และใช้เพื่อสร้างอินสแตนซ์Rack::Responseและส่งกลับไปยังไคลเอนต์ เบราว์เซอร์ใช้เวทย์มนตร์ในการพิมพ์ 'Hello, World!' ไปที่หน้าจอ

หากคุณต้องการดูว่าแฮชของสภาพแวดล้อมเป็นอย่างไรให้วางไว้puts envข้างใต้def call(env)ที่อยู่ภายใต้

น้อยที่สุดเท่าที่มันเป็นสิ่งที่คุณเขียนที่นี่เป็นโปรแกรมชั้น!

การสร้าง Rack Application โต้ตอบกับแฮชของ Incoming Environment

ในแอพชั้นวางเล็ก ๆ ของเราเราสามารถโต้ตอบกับenvแฮช (ดูที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับแฮชของสภาพแวดล้อม)

เราจะใช้ความสามารถสำหรับผู้ใช้ในการป้อนสตริงการสืบค้นของตัวเองลงใน URL ดังนั้นสตริงนั้นจะถูกนำเสนอในคำขอ HTTP ซึ่งห่อหุ้มเป็นค่าในหนึ่งในคู่ของคีย์ / ค่าของแฮชของสภาพแวดล้อม

แอพชั้นวางของเราจะเข้าถึงสตริงข้อความค้นหานั้นจากแฮชของสภาพแวดล้อมและส่งกลับไปยังไคลเอนต์ (เบราว์เซอร์ของเราในกรณีนี้) ผ่านทางเนื้อความในการตอบกลับ

จาก Rack docs บน Environment Hash: "QUERY_STRING: ส่วนของ URL คำขอที่ตามหลัง? ถ้ามีอาจว่างเปล่า แต่จำเป็นเสมอ!"

#./message_app.rb
class MessageApp
  def call(env)
    message = env['QUERY_STRING']
    [200, {}, [message]]
  end
end

ตอนนี้rackupและไปที่localhost:9292?hello( ?helloเป็นสตริงข้อความค้นหา) และคุณควรเห็น 'hello' แสดงผลในวิวพอร์ต

Rack Middleware

เราจะ:

  • ใส่ชิ้นส่วนของ Rack Middleware ลงใน codebase ของเรา - คลาส: MessageSetter ,
  • Environment hash จะเข้าคลาสนี้ก่อนและจะถูกส่งเป็นพารามิเตอร์: env ,
  • MessageSetterจะแทรก'MESSAGE'คีย์ลงในแฮช env ค่าของมันคือ'Hello, World!'ถ้าenv['QUERY_STRING']ว่างเปล่า;env['QUERY_STRING']ถ้าไม่,
  • ในที่สุดก็จะกลับ@app.call(env)- @appเป็นแอปต่อไปใน MessageApp'กอง':

ครั้งแรกรุ่น 'มือยาว':

#./middleware/message_setter.rb
class MessageSetter
  def initialize(app)
    @app = app
  end

  def call(env)
    if env['QUERY_STRING'].empty?
      env['MESSAGE'] = 'Hello, World!'
    else
      env['MESSAGE'] = env['QUERY_STRING']
    end
    @app.call(env)
  end
end

#./message_app.rb (same as before)
class MessageApp
  def call(env)
    message = env['QUERY_STRING']
    [200, {}, [message]]
  end
end

#config.ru
require_relative './message_app'
require_relative './middleware/message_setter'

app = Rack::Builder.new do
  use MessageSetter
  run MessageApp.new
end

run app

จากเอกสารRack :: Builderเราเห็นว่าRack::Builderใช้ DSL ขนาดเล็กไปจนถึงการสร้างแอปพลิเคชัน Rack ซ้ำ ๆ โดยทั่วไปหมายความว่าคุณสามารถสร้าง 'สแต็ค' ซึ่งประกอบด้วยมิดเดิ้ลแวร์หนึ่งรายการขึ้นไปและแอปพลิเคชัน 'ระดับล่าง' เพื่อส่งไปยัง คำขอทั้งหมดที่ดำเนินการผ่านแอปพลิเคชันระดับล่างของคุณจะได้รับการดำเนินการครั้งแรกโดย Middleware ของคุณ

#useระบุมิดเดิลแวร์ที่จะใช้ในสแต็ก มันใช้มิดเดิลแวร์เป็นอาร์กิวเมนต์

Middleware ของ Rack ต้อง:

  • มี Constructor ที่ใช้แอ็พพลิเคชันถัดไปในสแต็กเป็นพารามิเตอร์
  • ตอบสนองต่อcallวิธีการที่ใช้การแฮชของสภาพแวดล้อมเป็นพารามิเตอร์

ในกรณีของเรา 'Middleware' คือMessageSetter'constructor' เป็นinitializeเมธอดMessageSetter 'แอปพลิเคชันถัดไป' ในสแต็กคือMessageApp'ในกองเป็น

ดังนั้นที่นี่เพราะสิ่งที่Rack::Builderไม่ภายใต้ประทุนที่appข้อโต้แย้งของMessageSetter's วิธีคือinitializeMessageApp

(เอาหัวของคุณไปรอบ ๆ ด้านบนก่อนที่จะไปต่อ)

ดังนั้นมิดเดิ้ลของแต่ละชิ้นส่วนใหญ่จะ 'ส่งผ่าน' แฮชของสภาพแวดล้อมที่มีอยู่ไปยังแอปพลิเคชันถัดไปในโซ่ดังนั้นคุณจึงมีโอกาสที่จะกลายพันธุ์ของแฮชของสภาพแวดล้อมนั้นในมิดเดิลแวร์ก่อนส่งต่อไปยัง

#runรับอาร์กิวเมนต์ที่เป็นวัตถุที่ตอบสนอง#callและส่งกลับ Rack Response (อินสแตนซ์ของRack::Response)

สรุปผลการวิจัย

การใช้Rack::Builderคุณสามารถสร้างเครือข่ายของ Middlewares และคำขอใด ๆ ไปยังแอปพลิเคชันของคุณจะถูกประมวลผลโดย Middleware แต่ละรายการก่อนที่จะถูกประมวลผลโดยชิ้นสุดท้ายในสแต็ก (ในกรณีของเราMessageApp ) สิ่งนี้มีประโยชน์มากเพราะแยกขั้นตอนการประมวลผลคำขอที่แตกต่างออกไป ในแง่ของ 'การแยกความกังวล' มันไม่ค่อยสะอาดเท่าไหร่นัก!

คุณสามารถสร้าง 'ไปป์ไลน์ที่ร้องขอ' ซึ่งประกอบด้วยมิดเดิ้ลแวร์จำนวนมากที่จัดการกับสิ่งต่าง ๆ เช่น:

  • การรับรอง
  • การอนุญาต
  • เก็บเอาไว้
  • เครื่องประดับ
  • การตรวจสอบประสิทธิภาพและการใช้งาน
  • การดำเนินการ (จัดการกับคำขอจริงและให้การตอบกลับ)

(เหนือสัญลักษณ์แสดงหัวข้อย่อยจากคำตอบอื่นในหัวข้อนี้)

คุณมักจะเห็นสิ่งนี้ในแอปพลิเคชัน Sinatra แบบมืออาชีพ ซินาตร้าใช้ Rack! ดูที่นี่สำหรับความหมายของสิ่งที่ซินาตร้าIS !

ในฐานะที่เป็นโน้ตสุดท้ายเราconfig.ruสามารถเขียนในรูปแบบของมือสั้นสร้างฟังก์ชั่นที่เหมือนกัน (และนี่คือสิ่งที่คุณมักจะเห็น):

require_relative './message_app'
require_relative './middleware/message_setter'

use MessageSetter
run MessageApp.new

และเพื่อแสดงให้ชัดเจนยิ่งขึ้นว่าMessageAppกำลังทำอะไรอยู่นี่คือรุ่น 'มือยาว' ที่แสดงให้เห็นอย่างชัดเจนว่า#callกำลังสร้างอินสแตนซ์ใหม่Rack::Responseด้วยอาร์กิวเมนต์สามตัวที่จำเป็น

class MessageApp
  def call(env)
    Rack::Response.new([env['MESSAGE']], 200, {})
  end
end

ลิงค์ที่มีประโยชน์


1

ชั้นวาง - อินเทอร์เฟซ b / w เว็บและเซิร์ฟเวอร์แอป

Rack เป็นแพ็กเกจ Ruby ซึ่งมีอินเตอร์เฟสสำหรับเว็บเซิร์ฟเวอร์เพื่อสื่อสารกับแอปพลิเคชัน มันง่ายในการเพิ่มส่วนประกอบมิดเดิลแวร์ระหว่างเว็บเซิร์ฟเวอร์และแอพเพื่อปรับเปลี่ยนวิธีการทำงานของคำขอ / การตอบสนองของคุณ คอมโพเนนต์มิดเดิลแวร์อยู่ระหว่างไคลเอ็นต์และเซิร์ฟเวอร์ประมวลผลคำร้องขอขาเข้าและตอบกลับขาออก

ในคำพูดของคนธรรมดามันเป็นเพียงชุดของแนวทางสำหรับวิธีเซิร์ฟเวอร์และแอพพลิเค Rails (หรือเว็บแอปทับทิมอื่น ๆ ) ควรพูดคุยกับแต่ละอื่น ๆ

ในการใช้ Rack ให้ระบุ "แอป": วัตถุที่ตอบสนองต่อวิธีการโทรรับแฮชของสภาพแวดล้อมเป็นพารามิเตอร์และส่งกลับ Array ด้วยองค์ประกอบสามประการ:

  • รหัสตอบกลับ HTTP
  • Hash of headers
  • ร่างกายตอบสนองซึ่งจะต้องตอบสนองต่อแต่ละคำขอ

สำหรับคำอธิบายเพิ่มเติมคุณสามารถไปที่ลิงค์ด้านล่าง

1. https://rack.github.io/
2. https://redpanthers.co/rack-middleware/
3. https://blog.engineyard.com/2015/understanding-rack-apps-and-middleware
4. https://guides.rubyonrails.org/rails_on_rack.html#resources

ในรางเรามี config.ru เป็นไฟล์ชั้นวางคุณสามารถเรียกใช้ไฟล์ชั้นวางใด ๆ ด้วยrackupคำสั่ง 9292และพอร์ตเริ่มต้นสำหรับเรื่องนี้คือ เพื่อทดสอบสิ่งนี้คุณสามารถเรียกใช้rackupในไดเรกทอรีรถไฟของคุณและดูผลลัพธ์ นอกจากนี้คุณยังสามารถกำหนดพอร์ตที่คุณต้องการเรียกใช้ คำสั่งเรียกใช้ไฟล์ rack บนพอร์ตเฉพาะใด ๆ คือ

rackup -p PORT_NUMBER

1

ภาพแสดงชั้นวางระหว่างยูนิคอร์นและราง

Rack เป็นอัญมณีที่มีอินเตอร์เฟสที่ง่ายต่อการร้องขอ / ตอบกลับ HTTP แบบนามธรรม แร็คตั้งอยู่ระหว่างเฟรมเวิร์กเว็บ (Rails, Sinatra ฯลฯ ) และเว็บเซิร์ฟเวอร์ (ยูนิคอร์น, puma) เป็นอะแดปเตอร์ จากภาพด้านบนสิ่งนี้ทำให้เซิร์ฟเวอร์ยูนิคอร์นเป็นอิสระอย่างสมบูรณ์จากการรู้เกี่ยวกับรางและรางไม่ทราบเกี่ยวกับยูนิคอร์น นี่คือตัวอย่างที่ดีของการมีเพศสัมพันธ์หลวม , แยกของความกังวล

ภาพด้านบนมาจากการพูดคุยการประชุมทางรถไฟบนชั้นวาง https://youtu.be/3PnUV9QzB0gฉันขอแนะนำให้ดูเพื่อความเข้าใจที่ลึกซึ้งยิ่งขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.