Thứ Ba, 31 tháng 7, 2018

STRUTS 2 FRAMEWORK: VALIDATION ON DATA GIRD (HTML TABLE)

Tác giả: Lại Đức Toàn

 

Mục đích: Bài viết này sẽ hướng tới việc sử dụng Action Chaining kết hợp với Struts2 Validation Framework để validation dữ liệu trên giao diện và thực hiện báo lỗi chính xác tại vị trí trên lưới dữ liệu dưới sự hỗ trợ của Struts2 Validation Framework mà không cần viết code xử lý giao diện. Trong bài này, chúng ta sẽ tìm hiểu được kỹ năng về việc bắt lỗi khi xử lý trên một dòng và xử lý lỗi nhiều dòng khi input nhập liệu trên lưới đối với việc xử lý các thông tin của một đối tượng và các thông tin của nhiều đối tượng. Bài viết sẽ sử dụng chức năng update để thể hiện việc hiện thực vấn đề xử lý lỗi trực tiếp trên lưới dữ liệu

 

Các kiến thức yêu cầu

·         Khái niệm về ngôn ngữ lập trình Java, lập trình thao tác hướng đối tượng.

·         Khái niệm về lập trình web sử dụng J2EE hay JavaEE với các kiến thức về Servlet, JSP.

·         Các khái niệm về MVC, Struts Framework 1.x, các khái niệm cơ bản về Struts 2.x Framework.

·         Khái niệm về cơ chế Filter và RequestDispatcher trong Servlet

·         Khái niệm về Interceptor, Validations

·         Khái niệm về OGNL

 

Kiến thức tổng quát

·         Action Chaining

o   Là Chain Result và Chaining Interceptor, nhưng bài này chúng ta sẽ tìm hiểu việc xây dựng ứng dụng sử dụng Chain Result để bắt lỗi thông qua việc cập nhật thông tin dữ liệu trên lưới dữ liệu cho phép người dùng nhập liệu để lưu trữ đến database.

·         Chain Result

o   Là một result type thực hiện gọi một Action có sử dụng Interceptor Stack mặc định, Interceptor Stack ở đây chúng tôi nói đến là chainStack

o   Chain Result sẽ copy tất cả object trong ValueStack của action hiện hành đến ValueStack của action kế tiếp được thực thi trong khi thực hiện action kèm theo interceptor chain.

o   Cú pháp:

<action name=”action” class=”actionClass“>

    <interceptor-ref name=”interceptor1” />

    <result name=”success” type=”chain”>action1</result>

    <result name=”error”>/path1</result>

</action>

 

o   Chúng ta nên áp dụng nội dung này trong các ứng dụng mà chúng ta muốn duy trì giá trị form truyền dữ liệu từ request đầu tiên cho đến action cuối cùng để action này xử lý theo cách tương tự như RequestDispatcher đã thực hiện.

·         Tính năng đặc biệt trong cơ chế hoạt động của

o   OGNL

§  Khi OGNL tham chiếu tới một thành phần trong một Action được đặt trong ValueStack, OGNL sẽ duyệt từ Action nằm ở top của ValueStack cho đến khi hết stack. Trong quá trình duyệt để tìm kiếm, nếu thấy được Action đang tìm kiếm thì OGNL sẽ chọn Action và kết thúc quá trình tìm kiếm.

§  Nội dung lý thuyết trên cho thấy rằng nếu có 2 Action có cùng tên property giống nhau, thì OGNL sẽ chọn lấy property của Action nằm đầu(top-most) ValueStack.

o   Struts 2 Framework

§  Các parameters trùng tên nhau khi được set vào biến global trong Class Action sẽ không phải là một mảng String[] như chúng ta từng sử dụng trong parameter Servlet mà Struts 2 chỉ set vào biến global khi tên biến trùng với tên parameter và kiểu dữ liệu là “java.util.List”.

Xây dựng ứng dụng sử dụng Chain Result để bắt lỗi

·         Ứng dụng cho phép người dùng

o   Thực hiện cập nhật dữ liệu trên một dòng. Khi người dùng bấm nút Update từ một hàng trên bảng thì dữ liệu trên hàng đó sẽ được gửi về phía server. Sau đó, server sẽ thực hiện việc validation dữ liệu được gửi về. Nếu một trong những thông tin của dữ liệu xử lý không đúng ràng buộc được qui định thì server sẽ gửi lỗi về phía client và client thực hiện trình bày chính xác vị trí lỗi của thông tin mà người sử dụng đã nhập không chính xác.

o   Ngoài ra, cho phép người dùng sửa dữ liệu trên nhiều dòng cùng một lúc. Khi người dùng nhấn Update thì dữ liệu gửi về phía server xử lý. Và server sẽ thực hiện gửi lỗi cho client để trình bày chính xác từng nguyên nhân lỗi cụ thể trên từng field của từng dòng gây ra lỗi do sai về ràng buộc

o   Thực hiện xóa nhiều dòng cùng một lúc khi người dùng click chọn hàng loạt và nhấn nút Delete

·         Database chúng ta sử dụng trong bài này có cấu trúc như sau

·         Các ràng buộc dữ liệu

o   Người sử dụng có thể thay đổi password và lastname. Tuy nhiên, thông tin của hai trường này là bắt buộc phải nhập và chiều dài thông tin nhập không quá 20 kí tự.

o   Password không được chứa các kí tự đặc biệt.

·         Tools và công nghệ, thư viện yêu cầu để thực hiện ứng dụng

o   IDE: Netbeans 8.x (Recommendation 8.2).

o   JDK 6 update 22, JDK 7 update 51, JDK 8 update 66.

o   Server: Tomcat 6.0.26, Tomcat 7.x, Tomcat 8.x.

o   DBMS: SQL Server 2005 đến SQL Server 2017.

o   IDE Netbeans phải cài đặt plugin hỗ trợ Struts 2 Framework (Cụ thể bài này sử dụng Struts 2.3.15 – download tại địa chỉ http://plugins.netbeans.org/plugin/39218 ).

o   Driver kết nối database SQLServer (sqljdbc.jar hay sqljdbc4.jar – địa chỉ download https://docs.microsoft.com/en-us/sql/connect/jdbc/microsoft-jdbc-driver-for-sql-server?view=sql-server-2017 ).

o   Struts 2.x Framework (Struts 2. 3. 15 – download tại địa chỉ https://archive.apache.org/dist/struts/binaries/ ).

·         Các yêu cầu khi thực hiện bài này

o   Nắm rõ cách sử dung annotation kết hợp cấu hình struts.xml để xây dựng web application với Struts 2.x Framework (tham khảo tại địa chỉ: http://www.kieutrongkhanh.net/2017/07/video-su-dung-annotation-ket-hop-cau.html).

o   Nắm rõ cách thực hiện validation dữ liệu với annotation và trình bày message động dùng file properties với Struts 2.x Framework (tham khảo tại địa chỉ: http://www.kieutrongkhanh.net/2017/07/video-su-dung-annotation-ket-hop-cau.html).

o   Nắm rõ và đã xây dựng mô hình ứng dụng thao tác với các chức năng cơ bản của ứng dụng web, cụ thể ở đây là chức năng update hay delete (tham khảo tại địa chỉ http://www.kieutrongkhanh.net/2016/08/xay-dung-ung-dung-web-su-dung-mvc2-ket.htmlhttp://www.kieutrongkhanh.net/2016/10/video-delete-va-update-tren-luoi-voi.html hay http://www.kieutrongkhanh.net/2017/01/video-tiep-tuc-hoan-thien-chuc-nang-cho.html )

·         Các bước thực hiện chức năng Update cho từng dòng trên lưới

o   Nội dung bài viết chỉ tập trung vào chức năng chính, do vậy, một số nội dung liên quan đến xử lý chức năng cụ thể sẽ không thể hiện chi tiết trong nội dung hướng dẫn bên dưới.

o   Bước 1: Thực hiện chức năng Search

§  Chúng ta thực hiện được việc nhận giá trị tìm kiếm từ việc nhập liệu của người dùng. Thực hiện tìm kiếm dữ liệu từ Database và hiển thị lên phía browser.

§  Nội dung trang search.jsp có một số nội dung như sau

·         Thêm <s:head/> trong thẻ <head/> để hỗ trợ thông báo lỗi.

·         Set các giá trị mà người dùng nhập, mục đích để hiển thị lại dữ liệu người dùng vừa nhập

·         Chúng ta cần kiểm tra đúng username đã bị thay đổi thông tin sai để hiển thị lại trên máy người dùng. Nội dung của một dòng dữ liệu trên bảng như sau:

·         Bởi vì theme của form đang dùng là simple nên Framework Struts 2 không thể báo lỗi được, vì vậy cần phải sử dụng phương án Override lại theme bằng cách thêm attribute theme=”css_xhtml” trong thẻ <s:textfield/>

·         Chúng ta không sử dụng xhtml from vì các thành phần của xhtml form sẽ được bao lại trong một table, các properties cùng 1 dòng, mỗi textfield đặt ở  mỗi dòng, như vậy layout sẽ bị sai.

·         Riêng đối với css_xhtml form thì các thành phần bên trong sẽ được bao lại trong một <div/> và <div/> này nằm trong một ô vì thế layout của bảng sẽ không bị sai lệch.

§  Tạo Class Search Action để xử lý việc Search

§  Mapping chức năng vào struts.xml

o   Bước 2: Tạo Validator class để hỗ trợ việc validation

§  Tạo class có tên là UpdateAction và thực hiện extends ActionSuport. Sau đó, thực hiện Override hàm validate().

§  Bắt các lỗi nhập liệu mà chúng ta không mong muốn trong hàm validate(). Sử dụng kết hợp với ResourceBundle để lấy những message thông báo lỗi. Chúng ta sẽ thực hiện việc tạo file ActionClass.properties ở bước 3 để hỗ trợ việc lấy những message này.

o   Bước 3: Mapping lỗi để trình bày trên giao diện

Tạo tập tin properties có tên UpdateAction.properties để khai báo các message tương ứng với các message key đã được khai báo trong hàm validate() ở trên. Tập tin này nằm cùng pakage với Class UpdateAction.java:

o   Bước 4: cấu hình Chain Result trong tập tin struts.xml

§  Chúng ta sẽ thực hiện chain UpdateAction và SearchAction bằng cách thêm attribute type=“chain” như hình dưới:

§  Lưu ý

·         Nếu như SearchAction của chúng ta không extends ActionSuport thì có thể cấu hình như trên.

·         Ngược lại, chúng ta cần phải đặt giá trị của struts.xwork.chaining.copyFieldErrors trong file struts.properties là true hoặc khai báo một thẻ <constant/> trong tag chính <struts/> của tập tin struts.xml.

o   Trước hết, ta cần biết về resulttype nào thì phù hợp để sử dụng

§  Đối với trường hợp có extends ActionSupport này, chúng ta buộc phải để resulttype chain trong input để việc copy Field Error được diễn ra tự động sau khi đi qua hết các Actions bởi interceptor stack (mặc định là chainStack).

§  Nếu chúng ta không để resulttype chain trong input mà để type redirectAction thì  theo lý thuyết chúng ta phải cần phải chuyển được Field Errors từ UpdateAction sang SearchAction thông qua việc thêm parameter trong thẻ <result/> ở tập tin struts.xml như hình sau

o   Chúng ta chủ động viết được code nêu trên bởi vì class Action Support đã implement sẵn 02 phương thức getter/setter cho Field Error:

·         Mặc dù Class ActionSupport có hỗ trợ nhưng cách dùng này không thể thực hiện được. Kể cả việc ta dùng phương pháp dùng một biến global (khai báo đầy dủ setter/getter) ở cả hai Action để thực hiện chuyển tiếp thì cũng vẫn xảy ra lỗi sau

§  Khi thực hiện phương pháp chuyển Field Error trực tiếp

§  Khi thực hiện chuyển thông qua biến global có tên là userErrors

·         Vì vậy có thể khẳng định rằng, việc copy Field Error chỉ được thực hiện bởi phương pháp Action chaining thông qua interceptor.

o   Bây giờ, chúng ta áp dụng để thực hiện hai phương pháp nêu trên

§  Đặt giá trị của struts.xwork.chaining.copyFieldErrors trong file struts.properties là true

·         Tạo một file .properties có tên là struts và đặt trong thư mục \src\java nghĩa là cùng thư mục với tập tin struts.xml. Khi deploy ứng dụng xong, Framework Struts2 sẽ tự động tìmđọc các file .properties này trong thư mục WEB-INF

·         Sau đó thêm 1 key = value vào trong file .properties. Với dòng này, interceptor có tên là chain sẽ thực hiện việc copy Field Error

·         Thực hiện chaining interceptor(*) trong action searchLastName của tập tin struts.xml như hình

·         Tại sao cần phải làm như vậy? Chúng ta cần xem qua interceptor mặc định của Chain Result (chainStack)

o   Ta thấy rằng các interceptor trong chainStack và thứ tự các interceptor được đặt trong action searchLastName khác nhau về thứ tự đi vào và đi ra

o   Nếu như sử dụng chainStack thì Field Error sẽ được copy bởi interceptor chain trước rồi mới invoke hàm execute(). Như vậy ứng dụng sẽ không chạy được như mong muốn. Lí do là khi vào Action, Framework Struts2 sẽ thực hiện các bước theo thứ tự sau

§  Set các giá trị vào biến global trước thông qua setter.

§  Kiểm tra Field Error.

§  Gọi làm execute() nếu hasFieldErrors() == true, ngược lại return “input”.

§  Ngay từ bước thứ 2, chúng ta thấy rằng: SearchAction có Field Error và SearchAction trả ngay về “input”, như vậy hàm exucute() đã không được gọi để Search lại dữ liệu.

o   Còn nếu sử dụng phương pháp chaining interceptor thì việc copy Field Error sẽ được thực hiện sau cùng. Như vậy hàm execute() sẽ được invoke vì không có Error

·        Như chúng tôi đã đề cập đầu bài viết, chúng ta tập trung xây dựng ứng dụng theo hướng sử dụng Chain Result

§  Phương pháp thứ hai là khai báo một thẻ <constant/> trong tag chính <struts/> của tập tin struts.xml

·         Có  dòng này Field Error sẽ được copy sau khi tất cả các Action đã được invoke, chính vì vậy chúng ta không cần phải sử dụng Chaining Interceptor trong việc tránh xảy ra trường hợp hàm execute() không được invoke nếu có lỗi xảy ra.

·         Trong lý thuyết đã đề cập ở đầu bài viết này về OGNL, chúng ta nhận thấy

o   SearchAction không extends ActionSuport thì hiển nhiên sẽ không có Field Errors và OGNL sẽ đi tiếp tới Action thứ 2 để kiếm đó là UpdateAction.

o   SearchAction extends ActionSuport, lúc này SearchAction có Field Errors nhưng hoàn toàn không có dữ liệu vì các lỗi chúng ta bắt được nằm ở Field Errors của UpdateAcion, khi mà OGNL tìm đến thì nó sẽ tìm trong SearchAction trước, như vậy chúng ta không có thông tin về lỗi để thông báo cho người dùng. Vì thế chúng ta cần phải copy Field Errors.

o   Bước 5: Test ứng dụng

§  Thực hiện clean and build, deploy ứng dụng

§  Thực hiện chạy chương trình để test thử. Sau khi nhập giá trị vào ô search và nhấn nút search, chúng ta có màn hình kết quả như sau

§  Chúng ta thực hiện bỏ trống lastname ở dòng thứ 3 và nhấp nút Update, lỗi thông báo đúng vị trí mà chúng ta đã bỏ trống

§  Chúng ta thực hiện nhập password có kí tự đặc biệt và lastname dài hơn 20 kí tự ở dòng thứ 3 và nhấn nút Update. Kết quả chúng ta nhận được như hình bên dưới

·         Các bước thực hiện việc xử lý dữ liệu trên nhiều dòng cùng một lúc của lưới dữ liệu

o   Bước 1: Thực hiện chức năng Search

§  Phần nội dung của thân trang, cũng gần tương tự như update 1 dòng nhưng có một số thay đổi

·         Vị trí các link Delete sẽ là các checkbox

·         Ở dòng cuối có sẵn hai nút submit là Update và Delete. Việc có hai nút submit trong cùng 1 form là không thể vì chúng làm hai công việc khác nhau, vậy để tránh trường hợp này ta có hai cách

o   Cách đầu tiên, chúng ta thực hiện tách bảng thành hai form riêng, theo mô hình như hình sau

§  Khung đỏ tượng trưng cho một table với một dòng, hai cột.

§  Khung xanh lá tượng trưng cho một table khác nằm trong một ô của table đỏ, đồng thời mỗi table xanh có một nút submit riêng.

§  Cách này, quí vị tự hiện thực để xử lý

o   Cách thứ 2, chúng ta thực hiện override tên action của form tag bằng attribute “action” của submit tag

§  Đây là cách chúng ta sẽ thực hiện trong bài viết này

§  Khi sử dụng cách này chúng ta cần lưu ý đến version của thư viện Struts 2 mà chúng ta sử dụng để làm ứng dụng cụ thể như sau

§  Đối với phiên bản 2.3.15.2: sau khi bấm vào nút submit sẽ bị lỗi 404 (Nguồn: https://issues.apache.org/jira/browse/WW-4204).

§  Phiên bản <= 2.3.15.2: hoạt động bình thường

§  Phiên bản >= 2.3.15.2: mặc định không hoạt động. Để hoạt động được cần phải thêm dòng sau vào trong tag chính <struts/> của tập tin struts.xml (Nguồn: https://cwiki.apache.org/confluence/display/WW/S2-018)

§  Nội dung của trang search.jsp như sau

§  Chúng ta sẽ dùng attribute action trong button Delete, button Update sẽ gọi action mặc định của form

§  Với cách viết code cho nút Delete như trên, khi ứng dụng được thực thi chúng ta thực hiện View Source trên browser, chúng ta sẽ thấy code của nút Delete được phát sinh theo định dạng như sau

<input name=”action:overrideActionName” id=”formActionName_overrideActionName” type=”submit” value=”Delete” />

·         Chúng ta thấy được rằng dòng này sẽ được xem như là dữ liệu và sẽ được kèm vào trong parameter để đưa đến server.

·         Việc dùng tag <constant/> mà chúng ta đã nhìn thấy ở trên trong tag chính <struts/> của tập tin struts.xml  sẽ kích hoạt prefix mapping action (prefix action:). Khi đó struts 2 sẽ nhận ra rằng “chúng ta muốn gọi action có tên overrideActionName này thay vì gọi action mặc định của form hiện hành formActionName”.

§  Thực hiện tạo SearchAction và mapping chức năng tương tự như Search dữ liệu trên một dòng

o   Bước 2: thực hiện chức năng Delete

§  Chúng thực hiện implement phương thức để thực hiện detete dữ liệu trên nhiều dòng ở trong DAO

§  Thực hiện tạo DAO với hàm xử lý xóa với một list dữ liệu đưa vào

§  Thực hiện tạo Action Class để thực hiện cho việc Delete

·         Khai báo biến delete trùng tên với checkbox role ở trang jsp và có kiểu dữ liệu là List để Struts 2 có thể set các parameter trùng tên vào. Nếu list là rỗng thì Struts vẫn trả về action searchLastName bình thường     

·         Mapping chức năng delete thông qua tập tin struts.xml

o   Bước 3: Test thử ứng dụng

§  Clean and Build, Deloy và chạy ứng dụng

§  Thực hiện với chức năng Search

§  Thực hiện check vào các vị trí thứ 1, 5,7 và thực hiện click nút Delete

§  Lưu ý:

·         Đối với ứng dụng này việc dùng phương án override tên action vẫn không tốt vì có quá nhiều parameter không cần thiết được chuyển về Server

·         Chúng tôi giới thiệu phương án override cốt lõi để mở rộng hiểu biết thêm về thành phần Struts 2. Tuy nhiên, tối ưu nhất vẫn là phương án thứ nhất (tạo 2 form).

·         Thực hiện chức năng bắt lỗi trên nhiều dòng cùng một lúc trên lưới dữ liệu

o   Bước 1: Thực hiện chức năng Search

§  Chúng ta sẽ lấy trang jsp được tạo trong phần delete ở trên để sử dụng và thực hiện chỉnh sửa như sau

·         Như đã chúng ta đã biết, việc sử dụng tag <s:iterator/> trong việc tạo các dòng dữ liệu cho bảng thông tin thì các tag textfield chứa đựng khóa chính của bảng dữ liệu trên cùng cột sẽ trùng tên nhau

·         Nếu như có lỗi nhập liệu từ người dùng mà sử dụng phương pháp “update 1 dòng dữ liệu” thì cả 1 cột đều sẽ xuất hiện lỗi. Như vậy, chúng ta đã thực hiện thông báo lỗi sai cho người dung

·         Phương pháp giải quyết được chúng ta ứng dụng ở đây là đặt các tên trên cùng 1 cột sao cho là duy nhất(unique). Sau đó, chúng ta áp dụng cơ chế tự động nối chuỗi của OGNL như hình dưới, khi đó tên của textfield sẽ là tên của khóa chính trong bảng dữ liệu + chuỗi thông tin mà chúng ta cần xử lý. Ví dụ các tên phát sinh sẽ có định dạng kết xuất như sau toanld_password, hieunt_lastname

·         Bởi vì theme của form đang dùng là simple nên Struts 2 không thể báo lỗi được, vì vậy cần phải sử dụng phương án Override lại theme bằng cách thêm attribute theme=”css_xhtml” trong thẻ <s:textfield/>

o   Bước 2: thực hiện chức năng Update

§  Bổ sung thêm hàm update hàng loạt dữ liệu trong DAO bằng cách truyền tham số vào một List các giá trị cần tương tác

§  Tạo class UpdateAction phải implements ActionSuport, sau đó Override hàm validate()

§  Thực hiện bắt các lỗi nhập liệu mà chúng ta không mong muốn trong hàm validate(). Sử dụng kết hợp với ResourceBundle để lấy những message thông báo lỗi

§  Implement hàm validate()

·         Theo như trang jsp vừa thay đổi như trên, chúng ta không biết được chính xác tên của textfield là gì, vì tên textfield phụ thuộc vào username trong hàng của nó. Vì vậy, chúng ta sẽ dùng cách lấy parameter từ HttpServletRequest để lấy password và lastname. Chúng ta không sử dụng phương án lấy các parameter từ ActionContext như hình minh họa bên dưới vì sẽ xảy ra lỗi “ClassCastException: Ljava.lang.String; can not convert to java.lang.String”.

·         Chúng ta thực hiện lấy parameter từ HttpServletRequest vì tên parameter phụ thuộc vào tên username

·         Thực hiện kiểm tra password và addFieldErrors tương ứng

·         Thực hiện kiểm tra lastname, addFieldErrors tương ứng và set role

·         Kiểm tra lỗi, nếu có thì đưa user lỗi dữ liệu vào listUserError, ngược lại đưa vào list

§  Tạo tập tin ActionClass.properties

o   Bước 3: Cấu hình cho việc bắt lỗi khi chức năng được xử lý

§  Chúng ta sẽ thực hiện chain UpdateAction và SearchAction bằng cách thêm attribute type=”chain” như hình dưới, và cần chuyển danh sách các user lỗi dữ liệu qua SearchAction

§  Thực hiện bổ sung class SearchAction

·         Sau khi gọi DAO và có kết quả danh sách các user được tìm kiếm, chúng ta thực hiện việc set các user bị lỗi dữ liệu vào danh sách đó

o   Bước 4: Test thử ứng dụng

§  Clean and Build, Deloy và test ứng dụng

§  Thực hiện Search dữ liệu

§  Chỉnh sửa trên một dòng dữ liệu thứ 3 bằng cách xóa trắng ô Last name và nhấn Update. Chúng ta có kết quả như sau

§  Chỉnh sửa các dòng dữ liệu 02 và 03 với Password và Lastname, nhấn nút Update, chúng ta có kết quả như sau

§  Thực hiện với tất cả các hàng cùng với các cột

 

Qua những nội dung thực hiện ở bên trên bao gồm cách tiếp cận, cách implement cụ thể, quý vị đã nắm rõ hơn được cách thức hoạt động của Action Chaining, Validation và OGNL của Struts 2 Framework.

Chúng ta đã biết vận dụng Chain Result trong việc xây dựng ứng dụng để bắt lỗi và thông báo lỗi cụ thể dòng lỗi trên lưới khi người dùng đang nhập liệu

Chúng tôi hy vọng bài viết này sẽ giúp ích. Rất mong sự góp ý chân thành và chia sẻ của quý vị về chủ đề này. Hẹn gặp lại quý vị ở những chủ đề sau.

Không có nhận xét nào:

Đăng nhận xét