Câu hỏi Làm thế nào để tránh mã Java trong các tệp JSP?


Tôi mới sử dụng Java EE và tôi biết rằng một thứ giống như ba dòng sau

<%= x+1 %>
<%= request.getParameter("name") %>
<%! counter++; %>

là một cách học cũ của mã hóa và trong phiên bản JSP 2 có tồn tại một phương pháp để tránh mã Java trong các tệp JSP. Có thể ai đó vui lòng cho tôi biết các dòng JSP 2 thay thế và kỹ thuật này được gọi là gì?


1526
2017-07-05 07:24


gốc


Tôi không nghĩ rằng điều này là hợp lệ trong một tập tin jsp: <%! truy cập ++; %> - Koray Tugay
@Koray Tugay, miễn là biến truy cập được khai báo ở đâu đó trước khi sử dụng nó, thì nó chắc chắn là hợp lệ ... - Sheldon R.
@SheldonR. Điều này là hợp lệ: <% = counter ++%> hoặc điều này: <%! int counter = 0; int x = counter ++; %> nhưng không: <%! int counter = 0; truy cập ++; %> - Koray Tugay
@KorayTugay, ý tôi là nếu bộ đếm biến được khai báo trong một khối tập lệnh trước đó, nó sẽ hợp lệ trong một khối sau. Nhưng cuối cùng, các lập trình viên J2EE những ngày này nên sử dụng các biến EL thay vì tập lệnh, dù sao ... - Sheldon R.


Các câu trả lời:


Việc sử dụng tập lệnh (những, cái đó <% %> Những thứ trong JSP thực sự không được khuyến khích kể từ khi sinh taglibs (như JSTL) và EL (Ngôn ngữ biểu thức, những, cái đó ${} mọi thứ) cách trở lại vào năm 2001.

Những bất lợi chính của tập lệnh là:

  1. Khả năng sử dụng lại: bạn không thể tái sử dụng tập lệnh.
  2. Khả năng thay thế: bạn không thể làm cho tập lệnh trừu tượng.
  3. OO-khả năng: bạn không thể sử dụng thừa kế / thành phần.
  4. Khả năng ghi nợ: nếu scriptlet ném một nửa ngoại lệ, tất cả những gì bạn nhận được là một trang trống.
  5. Khả năng kiểm tra: tập lệnh không thể kiểm tra được đơn vị.
  6. Duy trì: mỗi saldo thêm thời gian là cần thiết để duy trì logic trộn lẫn / lộn xộn / trùng lặp.

mặt trời Bản thân Oracle cũng đề xuất trong Quy ước mã hóa JSP để tránh sử dụng tập lệnh bất cứ khi nào các chức năng tương tự có thể thực hiện bởi các lớp (tag). Dưới đây là một số trích dẫn về mức độ liên quan:

Từ Đặc tả JSP 1.2, bạn nên sử dụng Thư viện thẻ chuẩn JSP (JSTL) trong ứng dụng web của mình để trợ giúp giảm nhu cầu về tập lệnh JSP trong các trang của bạn. Các trang sử dụng JSTL nói chung, dễ đọc và duy trì hơn.

...

Có thể ở đâu, tránh tập lệnh JSP bất cứ khi nào thư viện thẻ cung cấp chức năng tương đương. Điều này làm cho các trang dễ đọc và duy trì hơn, giúp tách logic nghiệp vụ khỏi logic trình bày và làm cho các trang của bạn dễ dàng phát triển thành các trang theo phong cách JSP 2.0 (Đặc điểm kỹ thuật JSP 2.0 hỗ trợ nhưng tối ưu hóa việc sử dụng tập lệnh).

...

Theo tinh thần của việc áp dụng mẫu thiết kế model-view-controller (MVC) để giảm sự ghép nối giữa tầng trình bày từ logic nghiệp vụ, Tập lệnh JSP không nên được sử dụng để viết logic nghiệp vụ. Thay vào đó, các tập lệnh JSP được sử dụng nếu cần thiết để chuyển đổi dữ liệu (còn được gọi là "các đối tượng giá trị") được trả về từ việc xử lý các yêu cầu của máy khách thành một định dạng sẵn sàng thích hợp cho máy khách. Thậm chí sau đó, điều này sẽ được thực hiện tốt hơn với một bộ điều khiển phía trước servlet hoặc một thẻ tùy chỉnh.


Cách thay thế tập lệnh hoàn toàn phụ thuộc vào mục đích duy nhất của mã / logic. Thường thì mã này được đặt trong một lớp Java đầy đủ:

  • Nếu bạn muốn gọi tương tự Mã Java trên mỗi yêu cầu, ít hoặc nhiều hơn bất kể trang được yêu cầu, ví dụ: kiểm tra xem người dùng có đăng nhập hay không, sau đó triển khai bộ lọc và viết mã cho phù hợp doFilter() phương pháp. Ví dụ.:

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
            ((HttpServletResponse) response).sendRedirect("login"); // Not logged in, redirect to login page.
        } else {
            chain.doFilter(request, response); // Logged in, just continue request.
        }
    }
    

    Khi ánh xạ trên một <url-pattern>bao gồm các trang JSP quan tâm, sau đó bạn không cần sao chép cùng một đoạn mã trên tất cả các trang JSP.


  • Nếu bạn muốn gọi một số mã Java để tiền xử lý một yêu cầu, ví dụ: tải trước một số danh sách từ cơ sở dữ liệu để hiển thị trong một số bảng, nếu cần thiết dựa trên một số tham số truy vấn, sau đó triển khai servlet và viết mã cho phù hợp doGet() phương pháp. Ví dụ.:

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            List<Product> products = productService.list(); // Obtain all products.
            request.setAttribute("products", products); // Store products in request scope.
            request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); // Forward to JSP page to display them in a HTML table.
        } catch (SQLException e) {
            throw new ServletException("Retrieving products failed!", e);
        }
    }
    

    Cách này đối phó với ngoại lệ dễ dàng hơn. DB không được truy cập ở giữa hiển thị JSP, nhưng trước khi JSP được hiển thị. Bạn vẫn có khả năng thay đổi phản hồi bất cứ khi nào truy cập DB ném một ngoại lệ. Trong ví dụ trên, trang lỗi 500 mặc định sẽ được hiển thị mà bạn có thể tùy chỉnh theo <error-page> trong web.xml.


  • Nếu bạn muốn gọi một số mã Java để hậu xử lý một yêu cầu, ví dụ: xử lý gửi biểu mẫu, sau đó triển khai servlet và viết mã cho phù hợp doPost() phương pháp. Ví dụ.:

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = userService.find(username, password);
    
        if (user != null) {
            request.getSession().setAttribute("user", user); // Login user.
            response.sendRedirect("home"); // Redirect to home page.
        } else {
            request.setAttribute("message", "Unknown username/password. Please retry."); // Store error message in request scope.
            request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); // Forward to JSP page to redisplay login form with error.
        }
    }
    

    Cách này xử lý các điểm đến trang kết quả khác nhau dễ dàng hơn: hiển thị lại biểu mẫu có lỗi xác thực trong trường hợp xảy ra lỗi (trong ví dụ cụ thể này bạn có thể hiển thị lại nó bằng cách sử dụng ${message} trong EL), hoặc chỉ đến trang đích mong muốn trong trường hợp thành công.


  • Nếu bạn muốn gọi một số mã Java để điều khiển kế hoạch thực hiện và / hoặc đích của yêu cầu và phản hồi, sau đó triển khai servlet theo Mẫu điều khiển mặt trước của MVC. Ví dụ.:

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            Action action = ActionFactory.getAction(request);
            String view = action.execute(request, response);
    
            if (view.equals(request.getPathInfo().substring(1)) {
                request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
            } else {
                response.sendRedirect(view);
            }
        } catch (Exception e) {
            throw new ServletException("Executing action failed.", e);
        }
    }
    

    Hoặc chỉ áp dụng một khung MVC như JSF, Spring MVC, Wicket, vv để bạn kết thúc chỉ với một trang JSP / Facelets và một lớp Javabean mà không cần một servlet tùy chỉnh.


  • Nếu bạn muốn gọi một số mã Java để kiểm soát dòng chảy bên trong một trang JSP, sau đó bạn cần phải lấy một taglib kiểm soát luồng (hiện tại) Lõi JSTL. Ví dụ. hiển thị List<Product> trong một bảng:

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    ...
    <table>
        <c:forEach items="${products}" var="product">
            <tr>
                <td>${product.name}</td>
                <td>${product.description}</td>
                <td>${product.price}</td>
            </tr>
        </c:forEach>
    </table>
    

    Với các thẻ kiểu XML phù hợp độc đáo với tất cả HTML đó, mã có thể đọc được tốt hơn (và do đó có thể duy trì tốt hơn) so với một loạt các tập lệnh có các dấu ngoặc mở và đóng khác nhau ("Cái nẹp đóng này ở đâu?"). Một trợ giúp dễ dàng là định cấu hình ứng dụng web của bạn để ném một ngoại lệ bất cứ khi nào tập lệnh vẫn được sử dụng bằng cách thêm đoạn sau vào web.xml:

    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <scripting-invalid>true</scripting-invalid>
        </jsp-property-group>
    </jsp-config>
    

    Trong Facelets, sự kế thừa của JSP, là một phần của Java EE cung cấp khung MVC JSF, nó đã được không phải có thể sử dụng tập lệnh. Bằng cách này, bạn sẽ tự động bị buộc phải làm mọi thứ "đúng cách".


  • Nếu bạn muốn gọi một số mã Java để truy cập và hiển thị "backend" dữ liệu bên trong một trang JSP, sau đó bạn cần phải sử dụng EL (Expression Language), những ${} nhiều thứ. Ví dụ. hiển thị lại các giá trị đầu vào đã gửi:

    <input type="text" name="foo" value="${param.foo}" />
    

    Các ${param.foo} hiển thị kết quả của request.getParameter("foo").


  • Nếu bạn muốn gọi một số tiện ích Mã Java trực tiếp trong trang JSP (thường là public static phương pháp), sau đó bạn cần phải xác định chúng như là các hàm EL. Có một tiêu chuẩn chức năng taglib trong JSTL, nhưng bạn cũng có thể dễ dàng tự tạo các hàm. Dưới đây là ví dụ về cách JSTL fn:escapeXml rất hữu ích để ngăn chặn XSS  tấn công.

    <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    ...
    <input type="text" name="foo" value="${fn:escapeXml(param.foo)}" />
    

    Lưu ý rằng độ nhạy XSS là không có cách nào liên quan cụ thể đến Java / JSP / JSTL / EL / bất cứ điều gì, vấn đề này cần phải được đưa vào tài khoản mỗiwebapplication bạn phát triển. Vấn đề của tập lệnh là nó không cung cấp cách ngăn chặn nội trang, ít nhất là không sử dụng API Java chuẩn. Người kế nhiệm của JSP Facelets đã ẩn HTML, vì vậy bạn không cần phải lo lắng về các lỗ hổng XSS trong Facelets.

Xem thêm:


1853
2017-07-05 14:19



+1 Câu trả lời hay. Nhưng đừng đi theo giáo điều, đôi khi sử dụng kịch bản là ok, nhưng đó phải là ngoại lệ chứng minh quy tắc. - svachon
@svachon: Tập lệnh rất hữu ích để tạo mẫu / thử nghiệm nhanh. Theo như tôi biết, chỉ có một cách sử dụng sản xuất "hợp pháp" của một tập lệnh, cụ thể là <% response.getWriter().flush(); %> giưa </head> và <body> để cải thiện hiệu suất phân tích trang web trong trình duyệt web. Nhưng việc sử dụng này lần lượt hoàn toàn không đáng kể khi kích thước bộ đệm đầu ra ở phía máy chủ thấp (1 ~ 2KB). Xem thêm bài viết này. - BalusC
@ BalusC Một vài lần tôi đã bị mắc kẹt với các lớp java không theo mẫu getter / setter. IMHO là trường hợp scripltet thực hiện công việc. - svachon
@svachon: Tôi muốn bọc các lớp đó với các lớp javabean riêng và sử dụng chúng. - BalusC
Đó là một câu trả lời khá hay, nhưng các bộ phận doGet và doPost gây hiểu lầm. Những phương pháp này là để xử lý các yêu cầu cụ thể (HEAD, GET và POST) và không cho các yêu cầu "tiền xử lý" hoặc "hậu xử lý"! - MetroidFan2002


Là một biện pháp bảo vệ: Vô hiệu hóa tập lệnh cho tốt

Như câu hỏi khác đang thảo luận, bạn có thể và luôn luôn nên tắt tập lệnh trong web.xml bộ mô tả ứng dụng web.

Tôi sẽ luôn làm điều đó để ngăn chặn bất kỳ nhà phát triển nào thêm tập lệnh, đặc biệt là trong các công ty lớn hơn, nơi bạn sẽ mất tổng quan sớm hay muộn. Các web.xml cài đặt như sau:

<jsp-config>
  <jsp-property-group>
    <url-pattern>*.jsp</url-pattern>
     <scripting-invalid>true</scripting-invalid>
  </jsp-property-group>
</jsp-config>

207
2017-08-14 21:59



Điều này cũng vô hiệu hóa bình luận tập lệnh ?: <%-- comment that i don't want in the final HTML --%> . Tôi thấy hữu ích khi sử dụng những nhận xét này thay vì nhận xét HTML. - Mr Spoon
@MrSpoon đã theo dõi câu trả lời cho bạn. Theo câu trả lời này + nhận xét, điều này sẽ tắt tập lệnh <% %>, biểu thức tập lệnh <%! %>và khai báo tập lệnh <%= %>. Điều đó có nghĩa là chỉ thị <%@ %> và nhận xét <%-- --%> vẫn được bật và có thể sử dụng được, vì vậy bạn vẫn có thể đưa ra nhận xét và bao gồm. - Martin Carney
Việc vô hiệu hóa các chỉ thị tập lệnh sẽ là khoảng trắng cắt khủng khiếp là vô giá đối với việc tương tác với các hệ thống kế thừa mà không có khoảng trống thừa. - corsiKa


JSTL cung cấp thẻ cho điều kiện, vòng lặp, tập hợp, được, v.v. Ví dụ:

<c:if test="${someAttribute == 'something'}">
   ...
</c:if>

JSTL làm việc với các thuộc tính yêu cầu - chúng thường được đặt trong yêu cầu của Servlet, tiền đạo cho JSP.


102
2017-07-05 07:28



Tại sao bạn nói JSTL làm việc với các thuộc tính yêu cầu? Họ có thể làm việc với các thuộc tính trong bất kỳ phạm vi nào, phải không? - Koray Tugay


Tôi không chắc chắn nếu tôi nhận được điều này đúng.

Bạn nên đọc một cái gì đó về MVC. Spring MVC & Struts 2 là hai giải pháp phổ biến nhất.


56
2017-07-05 07:29



MVC có thể được thực hiện với servlets / jsp sử dụng nhiều kỹ thuật ở trên mà không có Spring hoặc Struts. - stepanian
Làm thế nào nó trả lời câu hỏi? - xyz


Bạn có thể sử dụng các thẻ JSTL cùng với các biểu thức EL để tránh trộn lẫn mã Java và HTML:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
    <head>
    </head>
    <body>

        <c:out value="${x + 1}" />
        <c:out value="${param.name}" />
    // and so on

    </body>
</html>

48
2017-07-05 08:45





Ngoài ra còn có các khuôn khổ dựa trên thành phần như Wicket tạo ra nhiều HTML cho bạn. Các thẻ kết thúc trong HTML là cực kỳ cơ bản và hầu như không có logic nào được trộn lẫn. Kết quả gần như là các trang HTML giống như trống với các phần tử HTML điển hình. Nhược điểm là có rất nhiều thành phần trong Wicket API để tìm hiểu và một số điều có thể khó đạt được theo những hạn chế đó.


32
2018-03-22 18:24





Trong mô hình kiến ​​trúc MVC, JSP đại diện cho lớp View. Nhúng mã java trong JSP được coi là một thực hành không tốt. Bạn có thể dùng JSTL, freeMarker, vận tốc với JSP là "công cụ tạo mẫu". Nhà cung cấp dữ liệu cho các thẻ đó phụ thuộc vào khuôn khổ mà bạn đang xử lý. Struts 2 và webwork như một triển khai cho mô hình MVC sử dụng OGNL "kỹ thuật rất thú vị để lộ ra Beans Properties cho JSP".


28
2018-02-04 10:41





Kinh nghiệm đã chỉ ra rằng JSP có một số thiếu sót, một trong số đó là khó tránh được việc đánh dấu sự trộn lẫn với mã thực tế.

Nếu bạn có thể, sau đó xem xét sử dụng một công nghệ chuyên ngành cho những gì bạn cần làm. Trong Java EE 6 có JSF 2.0, cung cấp rất nhiều tính năng đẹp bao gồm dán các hạt Java cùng với các trang JSF thông qua #{bean.method(argument)} tiếp cận.


25
2017-07-05 08:30



Câu trả lời cũ nhưng tôi không thể cưỡng lại rằng JSF là một trong những phát minh kinh khủng nhất trong không gian Java. Cố gắng tạo một liên kết đơn (HTTP GET like) và bạn sẽ hiểu tại sao. - Alex
@ Alex nhưng vẫn tốt hơn. Cảm thấy tự do để đề xuất một cái gì đó tốt hơn. - Thorbjørn Ravn Andersen


Wicket cũng là một thay thế hoàn toàn tách java từ html, do đó, một nhà thiết kế và lập trình viên có thể làm việc cùng nhau và trên các bộ mã khác nhau với ít sự hiểu biết lẫn nhau.

Nhìn vào Wicket.


23
2018-05-20 20:42