Công nghệ AJAX ra đời từ năm 2005, viết tắt của Asynchronous JavaScript and XML, cho phép các trang web ở phía client trao đổi dữ liệu với server mà không làm gián đoạn giao diện người dùng. AJAX là tên gọi chung của môt công nghệ, trong chương này chỉ thảo luận nền tảng AJAX của Microsoft.
Sơ lược về AJAX
Các trình duyệt web tương tác với server bằng cách tạo ra các yêu cầu cho trang thông qua hai phương thức là POST và GET. Server xử lí yêu cầu và gửi lại kết quả (HTML, script, css) đến trình duyệt. Mô hình truyền thống như sau:
Nhìn vào mô hình trên chúng ta thấy có hai trở ngại chính là:
- Do toàn bộ trang được gửi lại từ server nên tạo ra một gánh nặng không cần thiết. Ví dụ trong Contact Form của chúng ta, mỗi lần người dùng nhập thông tin và nhấn Send thì toàn bộ trang sẽ được gửi đến browser, trong khi chỉ có một vài thông tin người dùng nhập là thay đổi còn phần lớn các thành phần như menu, banner, table,…không đổi.
- Cũng vì toàn bộ trang được gửi trở lại từ server nên những cái đã có trong quá trình tương tác trước đó sẽ được thay bằng những cái mới, quá trình thay thế này sẽ làm cho trang cảm giác như “nhấp nháy”, một hiệu ứng gây khó chịu cho người dùng.
Microsoft đã tích hợp công nghệ AJAX vào nền tảng .NET (ASP.NET AJAX) để giải quyết những trở ngại trên. Một số lợi ích của ASP.NET AJAX:
- Chỉ một phần trang (được yêu cầu) được gửi trở lại từ server thay vì toàn bộ trang.
- Cung cấp phản hồi cho người dùng trong quá trình trang cập nhật.
- Cập nhật một phần trang và gọi các đoạn mã server đã được lập lịch bằng bộ định thời gian (timer).
- Truy cập đến các dịch vụ WCF, các phương thức trang và làm việc với dữ liệu chúng trả về.
Với AJAX, mô hình trao đổi giữa browsers và servers sẽ như sau:
Dùng ASP.NET AJAX
Khi bạn tạo ra một dự án ASP.NET thì mặc nhiên AJAX đã sẵn sàng được sử dụng. Ngoài ra, trong Toolbox cũng có mục AJAX Extensions cung cấp các controls liên quan tới AJAX:
Chúng ta sẽ lần lượt tìm hiểu và sử dụng các controls quan trọng trong AJAX Extensions như ScriptManager, ScriptManagerProxy, Timer, UpdatePanel, UpdateProgress.
UpdatePanel và ScriptManager
Để tránh tình trạng cập nhật lại toàn bộ trang dẫn đến hiện tượng “nhấp nháy” khó chịu, chúng ta có thể dùng UpdatePanel kết hợp với ScriptManager.
UpdatePanel
Giống như một khung chứa chỉ những phần của trang web được cập nhật. Một vài thuộc tính phổ biến của UpdatePanel:
Phương thức | Chức năng |
ChildrenAsTriggers | Xác định các controls có liên quan đến UpdatePanel có được cập nhật lại hay không. Mặc định là True. |
Triggers | Đây là một collection chứa hai thành phần là PostBackTrigger và AsyncPostBackTrigger; cái đầu có ích nếu bạn muốn cập nhật toàn bộ trang, cái sau dùng khi bạn muốn cập nhật UpdatePanel với một control bên ngoài nó. |
RenderMode | Có thể thiêt lập thuộc tính này đến Block hay Inline để xác định UpdatePanel sẽ là <div> hay <span> khi đến trình duyệt web. |
UpdateMode | Xác định UpdatePanel luôn được cập nhật hay cập nhật theo điều kiện. |
ContentTemplate | Là một thuộc tính quan trọng mặc dù không xuất hiện trong hộp Properties. Nó chứa các controls là con của UpdatePanel. |
Mặc dù có nhiều ưu điểm nhưng UpdatePanel vẫn có một vài phí tổn do dữ liệu chưa tối ưu, ảnh hưởng đến đường truyền Internet.
ScriptManager
Là chiếc cầu nối giữa client và server; quản lí các client-script (JavaScript), các phần cập nhật của trang, xử lí tương tác,…
ScriptManager chứa các thuộc tính quan trọng:
Phương thức | Chức năng |
AllowCustomErrorsRedirect | Xác định liệu các lỗi có xuất hiện trong quá trình một thao tác AJAX phát sinh lỗi tải trang. Mặc định là True. |
AsyncPostBackErrorMessage | Cho phép hiển thị các lỗi đến người dùng một cách thân thiện. |
EnablePageMethods | Cho phép các script (client) gọi các phương thức được định nghĩa trong trang. |
EnablePartialRendering | Cho phép một phần của trang cập nhật dùng UpdatePanel. |
EnableCdn | Cho phép ASP.NET liên kết đến các tập tin trên CDN (Content Delivery Network) của Microsoft. |
AjaxFrameworkMode | Cho phép các tích hợp các thư viện Microsoft AJAX. |
Scripts | Cho phép thêm các đoạn mã JavaScript được tải bởi client trong lúc thưc thi. |
CompositeScript | Giống Scripts nhưng các file JS trong CompositeScript được gộp trong một khối thống nhất. |
Services | Cho phép định nghĩa các dịch vụ WCF. |
Để hiểu hơn về UpdatePanel và ScriptManager chúng ta thực hiện phần thực hành sau.
Thực hành dùng UpdatePanel và ScriptManager
- Trong thư mục Demos, tạo một trang ASPX tên UpdatePanel.aspx bằng cách Add > Add New Item > MyBasePage. Trong chế độ Source của trang tìm đến thuộc tính Title trong <%@ page> và gõ dòng UpdatePanel Demo:
- Chuyển trang sang chế độ Design, kéo một Button và một Label từ Toolbox thả vào vùng cpMainContent( Button trước Label). Để thuộc tính Text của Label rỗng.
- Đến trình xử lí sự kiện Page_Load bằng cách nhấp đôi chuột vào vùng xám (vùng chỉ đọc) của trang và thêm dòng mã sau:
Mã C#
Label1.Text = System.DateTime.Now.ToString();
- Lưu, trở lại UpdatePanel.aspx và Ctrl + F5. Kết quả
- Ta thấy Label sẽ hiển thị thời gian gian hiện tại trên hệ thống máy tính (hay trên server). Nhấp chuột vào button vài lần sẽ thấy sau mỗi lần nhấp chuột trang sẽ nhấp nháy, gây cảm giác khó chịu.
- Đóng trình duyệt, trở lại trang UpdatePanel.aspx ở chế độ Source:
thêm UpdatePanel và ContentTemplate vào trang bằng cách gõ lại đoạn mã như sau:
- Phía trên <asp: UpdatePanel> thêm một ScriptManager được kéo từ mục AJAX Extensions của Toolbox:
- Lưu tất cả và Ctrl + F5. Nhấn lại button và trang sẽ không nhấp nháy nữa, mặc dù thời gian trong Label vẫn cập nhật bình thường.
Hỗ trợ phản hồi cho người dùng
Khi chúng ta thực hiện di chuyển một tập tin từ vị trí này đến vị trí khác, Windows sẽ thể hiện một thanh tiến trình như là cách thể hiện quá trình di chuyển của tập tin một cách trực quan đến người dùng. Với UpdatePanel, khi các yêu cầu được xử lí, người dùng sẽ chờ đợi trong một khoảng thời gian nào đó cho đến khi nhận kết quả từ server mà không cảm thấy điều gì và chúng ta có thể cung cấp một hình ảnh trực quan (giống thanh tiến trình) thể hiện quá trình xử lí đến người dùng bằng UpdateProgress control.
UpdateProgress
Chúng ta sẽ kết nối UpdateProgress đến UpdatePanel và ScriptManager bằng thuộc tính AssociatedUpdatePanelID. Nội dung của thuộc tính này được định nghĩa trong <ProgressTemplate> và sẽ được hiển thị trong khi UpdatePanel đang cập nhật dữ liệu. Nội dung trong <ProgresssTemplate> có thể là dòng text Xin vui lòng chờ vài giây hay một ảnh động .GIF nào đó (có thể vào trang www.ajaxload.info để tạo một ảnh động cho AJAX tương đối chuyên nghiệp bằng vài cú click chuột).
UpdateProgress cũng có vài thuộc tính quan trọng khác:
Phương thức | Chức năng |
DisplayAfter | Xác định thời gian (mili giây) chờ trước khi kết quả trả về được hiển thị. Mặc định là 500 mili giây. |
DynamicLayout | Xác định sự ẩn / hiện của control (Nếu giá trị là True tức là display : none; nếu giá trị False tức là visibility: hidden). |
Thực hành dùng UpdateProgress
- Mở ContactForm.ascx trong thư mục Controls ở chế độ Source. Bọc toàn bộ Table và Label ở cuối trong UpdatePanel và ContentTemplate (cách làm giống như bài thực hành đặt button và label ở trên)
<asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate> <table class="auto-style1" runat="server" id="FormTable"> ................... </table> <asp:Label ID="Message" runat="server" Text="Message Sent" Visible="false" /> </ContentTemplate> </asp:UpdatePanel>
- Lưu tất cả. Mở file Frontend.master từ thư mục MasterPages ở chế độ Source. Thêm ScriptManager vào giữa <form> và <div id = ‘PageWrapper’>:
- Mở trang UpdatePanel.aspx ở chế độ Source và xoá dòng ScriptManager đã thêm ở bài thực hành trên vì lúc này ScriptManager đã được thêm vào trang master. Lưu và đóng UpdatePanel.aspx.
- Mở trang Contact.aspx từ thư mục About và Ctrl + F5. Nhập thông tin vào Form và nhấn nút Send, trang sẽ không nhấp nháy.
- Thêm một file ảnh động (tên là ajax.gif) vào các thư mục Images của các theme Monochrome và DarkGrey
- Thêm đoạn CSS sau vào cuối hai file Monochrome.css và DarkGrey.css
- Lưu và đóng tất cả các file. Mở lại file ContactForm.ascx ở chế độ Source, kéo một UpdateProgress từ mục AJAX Extensions của Toolbox thả vào dưới </asp:UpdatePanel>, thiết lập thuộc tính AssociatedUpdatePanelID đến UpdatePanel1 (là ID của UpdatePanel)
- Thêm phần tử <ProgressTemplate> vào trong UpdateProgress với nội dung chứa trong <div> như sau:
- Lưu tất cả. Mở file ContactForm.ascx.vb và thêm dòng code sau vào sự kiện Click của nút Send:
Mã C#
System.Threading.Thread.Sleep(5000);
- Lưu và đóng file. Mở trang Contact.aspx trong thư mục About và Ctrl + F5. Nhập thông tin vào Form, nhấn Send và xem kết quả:
Timer control
Timer là một control trong mục AJAX Extensions của Toolbox. Timer hữu dụng khi chúng ta muốn trang tự động cập nhật nội dung trong UpdatePanel sau một khoảng thời gian nào đó, thường thấy ở các diễn đàn, các trang liên quan thị trường chứng khoán, quảng cáo,…
Để dùng Timer ta chỉ cần kích hoạt sự kiện Tick của nó cùng với khoảng thời gian. Chúng ta sẽ thực hiện bài thực hành sau về Timer để hiểu hơn về cách dùng control này.
Thực hành dùng Timer
- Mở trang UpdatePanel.aspx trong thư mục Demos và Ctrl + F5. Chúng ta thấy rằng, thời gian trên Label sẽ không thay đổi, nó chỉ cập nhật khi chúng ta nhấn Button. Chúng ta có thể làm cho thời gian trên Label cập nhật tự động sau mỗi giây (1000 mili giây) bằng Timer.
- Đóng trang và trở lại VS. Mở trang UpdatePanel.aspx ở chế độ Design và kéo Timer từ mục AJAX Extensions của Toolbox thả vào dưới Button và Label
- Nhấp đôi chuột vào Timer để đến sự kiện Tick của nó và gõ dòng code sau:
Mã C#
Label1.Text = System.DateTime.Now.ToString();
- Lưu và mở trang UpdatePanel.aspx ở chế độ Source, thêm các thuộc tính Interval và OnTick vào <asp: Timer> như sau:
- Lưu, Ctrl + F5 và quan sát thời gian trên Label.
Dịch vụ Web (Web services) và các phương thức trang (page methods)
Dịch vụ web là các phương thức có thể được gọi thông qua Internet và có thể trả về dữ liệu đến đoạn mã đang gọi. Các dịch vụ web được dựa trên các tiêu chuẩn nhất quán nên có thể trao đổi dữ liệu giữa các nền tảng hay hệ thống khác nhau – như trao đổi dữ liệu giữa website ASP.NET trên nền Windows và website PHP trên nền Linux, hay giữa một website ASP.NET và một browser (phía client) chạy JavaScript – một cách dễ dàng, nhanh chóng.
Windows Communication Foundation (WCF)
Để tạo các dịch vụ web trong website ASP.NET, chúng ta dùng WCF, một nền tảng của Microsoft, dùng để xây dựng các ứng dụng hướng dịch vụ với .NET Framework. WCF hỗ trợ một số công nghệ giao tiếp qua mạng như HTTP, .NET Remoting, Microsoft Message Queuing, và Enterprise Services. Do đó, WCF là một nền tảng tuyệt vời cho việc trao đổi dữ liệu trong những khía cạnh khác nhau, có thể là trên localhost, trên mạng cục bộ hay Internet (HTTP hay HTTPs).
Để xây dựng một dịch vụ Web, chúng ta cần thêm vào một dịch vụ WCF (có phần mở rộng là .svc) đến project. Trong một tập tin WCF (.svc) chứa hai thành phần là Service Contract và Operation Contract. Service Contract định nghĩa các dịch vụ, Operation Contract định nghĩa các phương thức cho các dịch vụ trong Service Contract. Đoạn mã một dịch vụ WCF đơn giản với một phương thức sẽ trông như thế này:
<ServiceContract(Namespace:="")> <AspNetCompatibilityRequirements( RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)> Public Class NameService <OperationContract()> Public Function HelloWorld(name As String) As String Return String.Format("Hello {0}", name) End Function End Class
Mã C#
[ServiceContract(Namespace = "")] [AspNetCompatibilityRequirements( RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class NameService { [OperationContract] public string HelloWorld(string name) { return string.Format("Hello {0}", name); } }
Chúng ta thấy rằng, để định nghĩa Service Contract chúng ta dùng thuộc tính ServiceContract (đặt trong dấu ngoặc nhọn). Thuộc tính AspNetCompatibilityRequirementsMode có hai giá trị: Allowed cho phép dịch vụ chạy trên website ASP.NET và NotAllowed thì ngược lại. Phương thức HelloWorld của dịch vụ được định nghĩa trong Operation Contract được định nghĩa bởi thuộc tính OperationContract. Tập tin dịch vụ WCF của chúng ta lúc này sẽ là NameService.svc.
Gọi dịch vụ từ phía client (trang HTML)
Để gọi dịch vụ từ một trang HTML nào đó, chúng ta cần đăng kí dịch vụ với ScriptManager control và sau đó gọi từ client. Ví dụ với tập tin dịch vụ NameService.svc ở trên được chứa trong thư mục WebService, chúng ta sẽ đăng kí đến ScriptManager như sau:
<asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="~/WebServices/NameService.svc" /> </Services> </asp:ScriptManager>
Sau khi đăng kí dịch vụ, chúng ta có thể gọi dịch vụ này từ client bằng một đoạn mã JavaScript như sau:
NameService.HelloWorld('ngocminhtran.com', helloWorldCallback); function helloWorldCallback(result) { alert(result); }
Để gọi phương thức trong dịch vụ, chúng ta dùng cú pháp:
Tên_dịch_vụ.phương_thức (tham số)
Ví dụ dịch vụ NameService gọi phương thức HelloWorld là:
NameService.HelloWorld (…)
Khi chúng ta gõ tên dịch vụ và phương thức, khi phương thức (ví dụ HelloWorld) mở dấu ngoặc sẽ xuất hiện danh sách chứa 4 tham số (mặc dù khi định nghĩa HellWorld ở NameService.svc chỉ có một tham số là name) như hình sau:
Tham số đầu (name) là tham số chúng ta định nghĩa trong tập tin WCF (NameService.svc); tham số thứ hai (onSuccess) là phương thức callack – phương thức chúng ta định nghĩa trong JavaScript, sẽ được gọi khi dịch vụ gọi thành công, trong ví dụ trên là helloWorldCallback; tham số thứ ba (onFail) cũng là một phương thức callback được gọi khi dịch vụ thực hiện cuộc gọi thất bại (như dịch vụ bị lỗi hay không hợp lệ); tham số cuối cùng (userContext) cho phép chúng ta chuyển thêm dữ liệu đến các phương thức callback.
Trên đây chỉ là một ví dụ đơn giản; trong thực tế dịch vụ có thể trao đổi với những đối tượng dữ liệu phức tạp hơn rất nhiều. Bây giờ, chúng ta sẽ thực hành cách tạo dịch vụ NameService, gọi thủ tục HelloWorld để hiểu rõ hơn dịch vụ này khi dùng trong một website ASP.NET.
Trong Visual Studio 2019 Community, WCF Service không được cài đặt mặc định nên chúng ta cần cài đặt thành phần này bằng cách vào File > New > Project…Trong hộp thoại Create a new project chọn Install more tools and features như các bước sau đây:
Thực hành tạo một dịch vụ WCF
- Tạo một thư mục tên WebServices trong Site (cùng cấp với các thư mục About, Demos, Controls,…).
- Nhấp chuột phải thư mục WebServices, chọn Add > Add New Item > AJAX – enabled WCF Service và đặt tên là NameService:
- Nhấn nút Add để thêm dịch vụ. Lúc này sẽ xuất hiện hai tập tin là NameService.svc trong thư mục WebServices và NameService.vb trong thư mục App_Code:
- Mở tập tin NameService.vb sẽ trông như sau:
- Xoá phương thức DoWork và thêm phương thức HelloWorld như sau:
Mã C#
[OperationContract] public string HelloWorld(string name) { return string.Format("Hello {0}", name); }
- Như vậy chúng ta đã tạo thành công một dịch vụ WCF (NameService.svc) và cách tạo một phương thức (HelloWorld). Nếu chúng ta yêu cầu dịch vụ trực tiếp từ trình duyệt web thì kết quả như sau:
Khi chúng ta tạo dịch vụ thành công, kế tiếp chúng ta sẽ đăng kí dịch vụ này đến ScriptManger, có hai cách:
- Đăng kí trong trong ScriptManger trong trang Master page. Nếu làm điểu này, chúng ta có thể dùng dịch vụ tại bất kì trang nào trong Site.
- Đăng kí dùng dịch vụ cho một trang nào đó bằng cách dùng lớp ScriptManagerProxy.
Trong bài thực hành tiếp theo này, chúng ta sẽ đăng kí dịch vụ NameService.svc chỉ dùng cho một trang nội dung nào đó bằng ScriptManagerProxy.
Thực hành đăng kí dịch vụ đến ScriptManagerProxy và gọi dịch vụ
- Tạo một trang ASPX để dùng dịch vụ tên WebServices.aspx trong thư mục Demos (chú ý chọn template là MyBasePage và tìm đến thuộc tính Title gõ tiêu đề trang, ví dụ Web services Demo):
- Mở trang WebServices.aspx ở chế độ Source và kéo một ScriptManagerProxy từ mục AJAX Extensios của Toolbox và thả vào vùng cpMainContent của trang:
- Trong ScriptManagerProxy thêm phần tử Services với phần tử con là ServiceReference có thuộc tính Path chứa đường dẫn đến tập tin dịch vụ NameService.svc. Bổ sung đoạn mã thêm Services vào ScriptManagerScript như sau:
- Thêm Input (Text) và Input (Button) từ mục HTML của Toolbox phía dưới </asp:ScriptManagerProxy> và chỉnh sửa các thuộc tính của hai Input này như sau:
Lưu ý rằng, đây là hai Input client (không có thuộc tính runat =”server”) sẽ được giữ nguyên khi đến trình duyệt web.
- Dưới hai Input thêm đoạn mã JavaScript sau:
- Thêm thuộc tính onClick đến Input (Button) để gọi phương thức helloWorld (phương thức của JavaScript) khi chúng ta click nó:
- Lưu tất cả và Ctrl + F5, gõ vài thông tin vào textbox và xem kết quả:
Các phương thức trang (Page Methods)
Các phương thức trang và các dịch vụ web WCF giống nhau ở một vài điểm:
- Cả hai có thể được gọi từ client (như JavaScript).
- Có thể gửi dữ liệu đến chúng và nhận lại.
- Có thể định nghĩa các phương thức callback thành công và thất bại (xem lại onSuccess và onFailed).
Khác biệt của phương thức trang là chúng có thể được định nghĩa trực tiếp trong trang (thay vì một file riêng như WCF) và có thể được gọi bởi một đoạn mã đang chạy trên cùng một trang. Do đó, các phương thức trang chỉ nên dùng khi cần tạo những chức năng nhỏ, đơn giản và phạm vi giới hạn trong một trang, nếu quy mô lớn hơn thì dùng dịch vụ WCF.
Để dùng phương thức trang, chúng ta phải thiết lập thuộc tính EnablePageMethods của ScriptManager control đến True và lưu ý rằng, thuộc tính này không có trong ScriptManagerProxy nên chỉ có thể dùng cho ScriptManager. Để định nghĩa phương thức trang, chúng ta làm theo hai bước:
- Tạo một phương thức tĩnh (static) và phạm vi là public trong tập tin vb sau đó áp dụng thuộc tính WebMethod đến phương thức này.
- Viết lệnh gọi JavaScript để gọi phương thức trên.
Thực hành tạo và gọi phương thức trang
- Mở trang master Frontend.master trong chế độ Source và thêm thuộc tính EnablePageMethods với giá trị True đến ScriptManager:
- Mở file WebServices.aspx.vb trong thư mục Demos, thêm đoạn định nghĩa phương thức trang HelloWorld vào trong lớp Demos_WebServices (chú ý có thuộc tính WebMethod)
- Thêm dòng Imports vào đầu trang để thêm namspace Web.UI, lúc này WebMethod sẽ không còn bị lỗi:
- Trở lại trang WebServices.aspx ở chế độ Source, thêm một Input (Button) dưới Input (Button) SayHello đã tạo ở trên và thay đổi một số thuộc tính như sau:
<input id="SayHelloPageMethod" type="button" value="Say Hello with a Page Method" onclick="helloWorldPageMethod();" />
- Thêm phương thức helloWorldPageMethod vào trong <script> ngay dưới phương thức helloWorldCallack:
- Lưu và Ctrl + F5. Nhập vài thông tin vào textbox và nhấn hai button (1 gọi WCF, 1 gọi phương thức trang) sẽ cho ra cùng kết quả.
Ý kiến bài viết