Sử dụng CoffeeScript trên máy chủ

Ứng dụng web trong Phần 3 đã dùng một từ khóa để thực hiện một tìm kiếm trên cả Google lẫn Twitter. Về phía máy khách của của ứng dụng, bạn chỉ cần tạo mô hình gi ả định các kết quả từ máy chủ. Để thực sự thực hiện các hàm như vậy, bạn cần phía máy chủ của ứng dụng để gọi các dịch vụ web được Google và Twitter cung cấp. Cả hai công ty đều cung cấp các dịch vụ t ìm kiếm rất đơn giản. Tất cả những gì bạn cần làm là tạo ra các yêu cầu HTTP GET với các dịch vụ tìm kiếm. Liệt kê 1 cho thấy một hàm tổng quát để tạo ra các yêu cầu HTTP GET

pdf9 trang | Chia sẻ: lylyngoc | Lượt xem: 1759 | Lượt tải: 1download
Bạn đang xem nội dung tài liệu Sử dụng CoffeeScript trên máy chủ, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
Sử dụng CoffeeScript trên máy chủ Gọi tất cả các dịch vụ web Ứng dụng web trong Phần 3 đã dùng một từ khóa để thực hiện một tìm kiếm trên cả Google lẫn Twitter. Về phía máy khách của của ứng dụng, bạn chỉ cần tạo mô hình giả định các kết quả từ máy chủ. Để thực sự thực hiện các hàm như vậy, bạn cần phía máy chủ của ứng dụng để gọi các dịch vụ web được Google và Twitter cung cấp. Cả hai công ty đều cung cấp các dịch vụ tìm kiếm rất đơn giản. Tất cả những gì bạn cần làm là tạo ra các yêu cầu HTTP GET với các dịch vụ tìm kiếm. Liệt kê 1 cho thấy một hàm tổng quát để tạo ra các yêu cầu HTTP GET. Liệt kê 1. Tìm nạp tài nguyên web http = require "http" fetchPage = (host, port, path, callback) -> options = host: host port: port path: path req = http.get options, (res) -> contents = "" res.on 'data', (chunk) -> contents += "#{chunk}" res.on 'end', () -> callback(contents) req.on "error", (e) -> console.log "Erorr: {e.message}" Ở phần đầu của kịch bản lệnh này là câu lệnh require (yêu cầu), mà bạn đã thấy tóm tắt trong Phần 1 của loạt bài này. Đây là cú pháp nhập khẩu mô đun của Node.js hoặc ít nhất là phiên bản CoffeeScript của nó. Phiên bản "nguyên gốc" sẽ là var http = require("http");. Trong bài này, bạn sẽ sử dụng một số mô đun lõi của Node.js. (Thông tin chi tiết về việc cách hoạt động của các mô đun nằm ngoài phạm vi của bài viết này). Tất cả các mô đun được sử dụng trong bài này đều có sẵn cho bạn nếu bạn đã cài đặt Node.js (xem Phần 1). Với ví dụ trong Liệt kê 1, bạn đang sử dụng mô đun http, có cung cấp một vài lớp và các hàm có ích cho cả việc tạo ra lẫn tiếp nhận các yêu cầu HTTP. Sau đó Liệt kê 1 định nghĩa một hàm fetchPage nhận bốn tham số:  Tên host (máy chủ) của tài nguyên.  port (cổng) của tài nguyên.  path (đường dẫn) của tài nguyên.  Một hàm callback (gọi lại). Bất kỳ kiểu hàm Vào/Ra (I/O) nào trong Node.js về bản chất sẽ là không đồng bộ và do đó sẽ cần một hàm callback để gọi khi nó hoàn thành. Hàm fetchPage nhận một hàm callback làm tham số thứ tư của mình. Hàm fetchPage sẽ sử dụng ba tham số đầu tiên để tạo ra một yêu cầu HTTP GET bằng cách sử dụng hàm get của mô đun http. Hàm fetchPage cũng nhận một hàm callback đã chuyển đi một cá thể ClientResponse. ClientResponse, là một đối tượng được định nghĩa trong mô đun http, thực hiện giao diện ReadableStream (một giao diện cốt lõi trong Node.js). Nó là một giao diện không đồng bộ nhận hai sự kiện: data (dữ liệu) và end (kết thúc). Hàm duy nhất của nó được sử dụng để đăng ký gọi lại đến các sự kiện này. Sự kiện dữ liệu xảy ra khi tiếp nhận dữ liệu từ tài nguyên mà bạn đã tạo ra yêu cầu HTTP GET để lấy. Tất cả dữ liệu có thể được trả về cùng một lúc từ tài nguyên, nhưng phổ biến hơn, dữ liệu sẽ được gửi theo các bó (chunk). Khi nhận được mỗi bó, sự kiện dữ liệu được khởi động và hàm gọi lại được gọi ra. Bạn đã tạo ra một biến có tên là contents (các nội dung); mỗi khi bạn nhận được một bó khác, bạn chỉ cần gắn thêm nó vào contents. Khi đã nhận được tất cả dữ liệu, sự kiện end được khởi động. Bây giờ bạn có tất cả dữ liệu, vì vậy bạn có thể chuyển contents trở lại cho hàm callback đã được chuyển tới hàm fetchPage. Khi đã định nghĩa hàm đa năng này, chúng ta hãy tạo ra một số hàm chuyên dụng hơn cho các API tìm kiếm của Google và Twitter, như trong Liệt kê 2. Liệt kê 2. Các hàm tìm kiếm Google và Twitter googleSearch = (keyword, callback) -> host = "ajax.googleapis.com" path = "/ajax/services/search/web?v=1.0&q=#{encodeURI(keyword)}" fetchPage host, 80, path, callback twitterSearch = (keyword, callback) -> host = "search.twitter.com" path = "/search.json?q=#{encodeURI(keyword)}" fetchPage host, 80, path, callback Có hai hàm được định nghĩa trong Liệt kê 2:  googleSearch, nhận một keyword (từ khóa) và một hàm callback. Nó ấn định máy chủ, tạo động đường dẫn bằng cách sử dụng phép nội suy chuỗi của CoffeeScript và sau đó sử dụng hàm fetchPage.  twitterSearch, rất giống với googleSearch nhưng có máy chủ và giá trị đường dẫn khác. Đối với cả hai giá trị đường dẫn, bạn sử dụng phép nội suy chuỗi và hàm encodeURI tiện dụng của JavaScript để xử lý bất kỳ khoảng trống hoặc các ký tự đặc biệt khác nào. Vì bạn có các hàm tìm kiếm này, nên bạn có thể tạo ra một hàm chuyên dụng cho một kịch bản tìm kiếm kết hợp. Về đầu trang Kết hợp các hàm không đồng bộ Có một vài cách để bạn có thể thực hiện tìm kiếm kết hợp trên Google và Twitter. Bạn có thể gọi googleSearch và sau đó, trong hàm callback, gọi twitterSearch, hoặc ngược lại. Tuy nhiên, kiến trúc không đồng bộ/gọi lại của Node.js cho phép bạn làm những việc này một cáchđẹp đẽ hơn và hiệu quả hơn. Liệt kê 3 hiển thị tìm kiếm kết hợp. Liệt kê 3. Tìm kiếm cả Google lẫn Twitter combinedSearch = (keyword, callback) -> data = google : "" twitter : "" googleSearch keyword, (contents) -> contents = JSON.parse contents data.google = contents.responseData.results if data.twitter != "" callback(data) twitterSearch keyword, (contents) -> contents = JSON.parse contents data.twitter = contents.results if data.google != "" callback(data) Hàm combinedSearch có một chữ ký mà bây giờ đã quen thuộc: nhận một từ khóa và một hàm gọi lại. Sau đó, nó tạo ra một cấu trúc dữ liệu cho các kết quả tìm kiếm kết hợp được gọi là data. Đối tượng data có một trường google và một trường twitter, cả hai đều được khởi tạo như các chuỗi rỗng. Bước tiếp theo gọi hàm googleSearch. Trong hàm gọi lại của nó, bạn phân tích cú pháp các kết quả từ Google bằng cách sử dụng hàm JSON.parse tiêu chuẩn. Văn bản JSON được trả về từ Google được phân tích cú pháp thành một đối tượng JavaScript. Sử dụng đối tượng này để thiết lập giá trị của trường data.google. Sau khi gọi googleSearch, hãy gọi twitterSearch. Hàm callback của nó rất giống với hàm callback dùng cho googleSearch. Điều quan trọng cần hiểu rằng trong cả hai hàm gọi lại mà bạn kiểm tra để xem liệu bạn có dữ liệu từ hàm gọi lại khác không. Bạn không biết hàm gọi lại nào sẽ hoàn thành trước tiên. Vì vậy, hãy kiểm tra để xem liệu bạn có dữ liệu từ cả Google lẫn Twitter không. Một khi bạn làm điều đó, bạn gọi hàm callback đã được chuyển tới hàm combinedSearch. Bây giờ bạn có một hàm sẽ tìm kiếm cả Google lẫn Twitter và đưa ra các kết quả kết hợp. Nhiệm vụ tiếp theo là trưng ra hàm này cho trang web mà bạn đã tạo ra trong Phần 3 của loạt bài này. Tất cả những gì bạn phải làm là viết một máy chủ web. Về đầu trang Một máy chủ web CoffeeScript Lúc này, bạn có:  Một trang web có thể gửi các từ khoá và hiển thị các kết quả tìm kiếm.  Một hàm có thể dùng một từ khóa và tạo ra các kết quả tìm kiếm từ Google và Twitter. Cái gì gắn kết những thứ này lại với nhau? Bạn có thể gọi nó là một máy chủ web, máy chủ ứng dụng, hoặc thậm chí phần mềm trung gian. Bất kể bạn muốn gọi nó là gì, không cần dùng nhiều mã CoffeeScript để viết nó. Máy chủ web cần đáp ứng hai mục đích. Rõ ràng, nó cần nhận các yêu cầu cho việc tìm kiếm kết hợp. Nó cũng cần cung cấp tài nguyên tĩnh mà bạn đã tạo ra trong Phần 3. Bạn đang tạo ra một ứng dụng web, do đó, bạn phải chú ý đến các quy định. Các cuộc gọi tìm kiếm phải đi tới cùng một nơi đã tạo ra trang web. Trước tiên hãy xử lý tài nguyên tĩnh. Liệt kê 4 cho thấy một hàm để cung cấp tài nguyên tĩnh. Liệt kê 4. Cung cấp tài nguyên tĩnh path = require "path" fs = require "fs" serveStatic = (uri, response) -> fileName = path.join process.cwd(), uri path.exists fileName, (exists) -> if not exists response.writeHead 404, 'Content-Type': 'text/plain' response.end "404 Not Found #{uri}!\n" return fs.readFile fileName, "binary", (err,file) -> if err response.writeHead 500, 'Content-Type': 'text/plain' response.end "Error #{uri}: #{err} \n" return response.writeHead 200 response.write file, "binary" response.end() Hàm serveStatic xử lý các yêu cầu về nguồn tài nguyên tĩnh trong ứng dụng web. Lưu ý rằng bạn cần sử dụng thêm hai mô đun Node.js nữa:  path chỉ đơn giản là một thư viện tiện ích để xử lý các đường dẫn tệp.  Hệ thống tệp, hoặc fs, cung cấp tất cả Vào/Ra của tệp trong Node.js và về cơ bản là một trình bao gói dựa trên các hàm POSIX tiêu chuẩn. Hàm serveStatic nhận hai tham số:  uri về bản chất là một đường dẫn tương đối đến tệp tĩnh đang được một trình duyệt web yêu cầu.  Một đối tượng ServerResponse, là một kiểu khác được định nghĩa trong mô đun http. Ngoài nhiều thứ khác, nó mang lại cho bạn một luồng để ghi dữ liệu vào bất cứ thứ gì đã tạo ra yêu cầu HTTP GET về tài nguyên. Trong hàm serveStatic, chuyển đường dẫn tương đối đến tệp thành một đường dẫn tuyệt đối bằng cách sử dụng process.cwd. Đối tượng process (tiến trình) là một đối tượng chung (global), đại diện cho tiến trình hệ thống mà Node.js đang chạy trên đó. Phương thức cwd của nó cung cấp thư mục làm việc hiện tại. Sử dụng mô đun path để kết hợp thư mục làm việc hiện tại và đường dẫn tương đối đến tệp mà bạn muốn; kết quả là đường dẫn tuyệt đối. Với đường dẫn tuyệt đối, bạn có thể sử dụng lại mô đun path để kiểm tra xem tệp có tồn tại hay không. Việc kiểm tra xem tệp có tồn tại không liên quan đến I/O (Vào/Ra), cho nên nó là một hàm không đồng bộ. Chuyển cho nó fileName (tên tệp) và một hàm gọi lại. Hàm gọi lại nhận một giá trị Boolean, cho bạn biết liệu tệp tồn tại hay không. Nếu nó không tồn tại, thì bạn cần viết một thông báo HTTP 404 " không tìm thấy tệp". Nếu tệp tồn tại, thì bạn cần đọc nội dung của nó bằng cách sử dụng mô đun fs và phương thức readFile, phương thức readFile là không đồng bộ. Nó nhận fileName, một kiểu và một hàm gọi lại. Hàm gọi lại nhận hai thông số:  Một tham số lỗi cho biết bất kỳ vấn đề gì khi đọc tài nguyên từ hệ thống tệp. Nếu có vấn đề, một thông báo lỗi HTTP 500 được trả ngược lại tới phía máy khách.  Nếu không có vấn đề gì, một thông báo HTTP 200 OK được viết và các nội dung của tệp được gửi lại tới phía máy khách. Hàm này xử lý trường hợp cung cấp các tệp tĩnh tương đối dễ dàng. Phần tiếp theo sẽ thảo luận về kịch bản khó hơn, ở đó bạn cần đáp ứng động với một yêu cầu tìm kiếm. Về đầu trang Các đáp ứng động và máy chủ Ví dụ máy chủ web chủ yếu xử lý các yêu cầu tài nguyên tĩnh và các yêu cầu tìm kiếm động. Chiến lược là sử dụng một URL cụ thể để xử lý các yêu cầu tìm kiếm và sau đó chuyển các yêu cầu khác tới hàm serveStatic. Hãy sử dụng URL tương đối là /doSearch cho các yêu cầu tìm kiếm. Liệt kê 5 cho thấy mã của máy chủ web. Liệt kê 5. Máy chủ web CoffeeScript url = require "url" server = http.createServer (request, response) -> uri = url.parse(request.url) if uri.pathname is "/doSearch" doSearch uri, response else serveStatic uri.pathname, response server.listen 8080 console.log "Server running at " Một lần nữa, kịch bản lệnh này bắt đầu bằng cách nạp một mô đun Node.js. Mô đun url là một thư viện có ích để phân tích cú pháp các URL. Bước tiếp theo là tạo ra máy chủ web bằng cách sử dụng mô đun http mà bạn nạp trong Liệt kê 1. Sử dụng phương thức createServer của mô đun đó, phương thức này nhận một hàm gọi lại sẽ được gọi mỗi khi có yêu cầu được gửi đến máy chủ web. Hàm gọi lại có hai tham số: một cá thể ServerRequest và một cá thể ServerResponse. Cả hai kiều đều được định nghĩa trong mô đun http. Trong hàm gọi lại này, việc phân tích cú pháp URL của yêu cầu đã được gửi đến máy chủ bằng cách sử dụng phương thức parse (phân tích cú pháp) của mô đun url. Việc này sẽ cung cấp cho bạn một đối tượng URL và bạn có thể sử dụng thuộc tính pathname (tên đường dẫn) của nó để nhận được đường dẫn tương đối. Nếu pathname là /doSearch, bạn gọi hàm doSearch (được thảo luận dưới đây). Nếu không, hãy gọi hàm serveStatic từ Liệt kê 5. Liệt kê 6 cho thấy cách doSearch hoạt động. Liệt kê 6. Xử lý các yêu cầu tìm kiếm doSearch = (uri, response) -> query = uri.query.split "&" params = {} query.forEach (nv) -> nvp = nv.split "=" params[nvp[0]] = nvp[1] keyword = params["q"] combinedSearch keyword, (results) -> response.writeHead 200, 'Content-Type': 'text/plain' response.end JSON.stringify results Hàm doSearch phân tích cú pháp chuỗi truy vấn đối với URL, mà có thể tìm thấy nó trong thuộc tính query (truy vấn) của đối tượng uri. Chia nhỏ chuỗi truy vấn này ra bằng cách tách chuỗi này tại vị trí các ký tự &. Sau đó chia tách từng chuỗi con tại vị trí dấu bằng để nhận được các cặp giá trị- tên từ chuỗi truy vấn. Hãy lưu trữ từng cặp tên-giá trị vào đối tượng params. Lấy ra tham số "q" để có được từ khóa mà bạn muốn tìm kiếm. Chuyển từ khóa này tới hàm combinedSearch trong Liệt kê 3. Bạn phải chuyển cho hàm này một hàm gọi lại. Hàm gọi lại ví dụ chỉ cần viết một thông báo HTTP 200 OK và chuyển các kết quả tìm kiếm thành một chuỗi bằng cách sử dụng hàm tiêu chuẩn JSON.stringify. Đó là tất cả những gì bạn cần cho máy chủ. Trong phần tiếp theo, hãy xem cách móc nối máy chủ này vào mã máy khách trong Phần 3 của loạt bài này. Về đầu trang Gọi máy chủ tìm kiếm Trong Phần 3 bạn đã có một lớp MockSearch đã sử dụng dữ liệu giả đinh để cung cấp các kết quả tìm kiếm. Bây giờ bạn sẽ định nghĩa một lớp mới để thực hiện một tìm kiếm thực gọi máy chủ tìm kiếm. Liệt kê 7 cho thấy lớp tìm kiếm mới. Liệt kê 7. Lớp tìm kiếm thực class CombinedSearch search: (keyword, callback) -> xhr = new XMLHttpRequest xhr.open "GET", "/doSearch?q=#{encodeURI(keyword)}", true xhr.onreadystatechange = -> if xhr.readyState is 4 if xhr.status is 200 response = JSON.parse xhr.responseText results = google: response.google.map (result) -> new GoogleSearchResult result twitter: response.twitter.map (result) -> new TwitterSearchResult result callback results xhr.send null Lớp CombinedSearch có một phương thức duy nhất là search có chữ ký giống như phương thức search của MockSearch. Nó nhận một từ khóa và một hàm gọi lại. Bên trong hàm này:  Sử dụng XMLHttpRequest, một "người bạn cũ" quen thuộc của bất kỳ nhà phát triển web nào, để tạo một yêu cầu HTTP đến máy chủ bằng cách sử dụng đường dẫn /doSearch và từ khóa đã được chuyển đến hàm này.  Khi bạn nhận được một phản hồi, hãy phân tích cú pháp của nó bằng cách sử dụng JSON.parse.  Tạo một đối tượng các kết quả với các trường google và twitter. Tạo ra chúng bằng cách sử dụng các lớp GoogleSearchResult và TwitterSearchResult trong Phần 3.  Chỉ cần chuyển các kết quả trở lại hàm callback. Bây giờ, bạn chỉ cần sử dụng lớp doSearch này trong phương thức MockSearch. Liệt kê 8 cho thấy cách sử dụng lớp CombinedSearch. Liệt kê 8. Sử dụng lớp CombinedSearch @doSearch = -> $ = (id) -> document.getElementById(id) kw = $("searchQuery").value appender = (id, data) -> data.forEach (x) -> $(id).innerHTML += "#{x.toHtml()}" ms = new CombinedSearch ms.search kw, (results) -> appender("gr", results.google) appender("tr", results.twitter) Nếu bạn so sánh Liệt kê 8 với doSearch trong Phần 3, bạn sẽ không thấy nhiều sự khác biệt. Điều duy nhất có khác là ở dòng thứ bảy. Thay vì tạo một cá thể là MockSearch thì bạn tạo một cá thể là CombinedSearch. Những thứ khác đều như nhau. Bạn nhận được từ khóa từ trang web, gọi tìm kiếm và sau đó gắn thêm các kết quả bằng cách gọi phương thức toHtml của từng đối tượng SearchResult. Hình 1 cho thấy ứng dụng web với các kết quả tìm kiếm "sống" đến từ máy chủ. Hình 1. Chạy ứng dụng web ví dụ Để nhận được các thay đổi bạn đã làm đối với mã phía máy khách, bạn cần biên dịch lại nó bằng coffee -c search.coffee. Để chạy ứng dụng, hãy sử dụng coffee search-server.coffee. Sau đó, bạn có thể mở một trình duyệt tới địa chỉ và thử một số truy vấn khác nhau. Về đầu trang Kết luận Trong bài này, bạn đã hoàn thành ứng dụng web bằng cách xây dựng thành phần phía máy chủ để bổ sung cho mã phía máy khách trong Phần 3. Giờ đây, sau khi hoàn thành phần cuối của loạt bài này, bạn đã có một ứng dụng hoàn chỉnh—tất cả được viết bằng CoffeeScript. Bạn đã sử dụng rất nhiều tính năng của Node.js đã cho phép bạn sử dụng CoffeeScript như một công nghệ phía máy chủ. Một hạn chế đáng trách của Node.js là nó không chặn các hàm callback khi gọi từ tầng này sang tầng khác. Điều này có thể khiến cho việc tư duy sắp xếp/phân loại khó khăn và với cú pháp dài dòng của JavaScript thậm chí còn khiến mọi việc phức tạp hơn. CoffeeScript không thay đổi nhu cầu sử dụng của tất cả các hàm callback, nhưng với cú pháp rõ ràng của nó đã giúp dễ viết và dễ hiểu những đoạn mã như thế này hơn một chút.
Tài liệu liên quan