Servlet phục vụ nội dung tĩnh


145

Tôi triển khai một ứng dụng web trên hai thùng chứa khác nhau (Tomcat và Jetty), nhưng các máy chủ mặc định của chúng để phục vụ nội dung tĩnh có cách xử lý cấu trúc URL khác mà tôi muốn sử dụng ( chi tiết ).

Do đó, tôi đang tìm cách đưa một servlet nhỏ vào ứng dụng web để phục vụ nội dung tĩnh của riêng nó (hình ảnh, CSS, v.v.). Các servlet nên có các thuộc tính sau:

  • Không phụ thuộc bên ngoài
  • Đơn giản và đáng tin cậy
  • Hỗ trợ cho If-Modified-Sincetiêu đề (tức là getLastModifiedphương thức tùy chỉnh )
  • (Tùy chọn) hỗ trợ mã hóa gzip, etags, ...

Là một servlet có sẵn ở đâu đó? Gần nhất tôi có thể tìm thấy là ví dụ 4-10 từ sách servlet.

Cập nhật: Cấu trúc URL tôi muốn sử dụng - trong trường hợp bạn đang tự hỏi - chỉ đơn giản là:

    <servlet-mapping>
            <servlet-name>main</servlet-name>
            <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>/static/*</url-pattern>
    </servlet-mapping>

Vì vậy, tất cả các yêu cầu nên được chuyển đến servlet chính, trừ khi chúng dành cho staticđường dẫn. Vấn đề là servlet mặc định của Tomcat không đưa ServletPath vào tài khoản (vì vậy nó tìm các tệp tĩnh trong thư mục chính), trong khi Jetty thì có (vì vậy nó tìm trong staticthư mục).


Bạn có thể giải thích về "cấu trúc URL" bạn muốn sử dụng không? Tự lăn, dựa trên ví dụ được liên kết 4-10, có vẻ như là một nỗ lực tầm thường. Tôi đã thực hiện nó rất nhiều lần ...
Stu Thompson

Tôi đã chỉnh sửa câu hỏi của mình để xây dựng cấu trúc URL. Và vâng, cuối cùng tôi đã lăn bánh. Xem câu trả lời của tôi dưới đây.
Bruno De Fraine

1
Tại sao bạn không sử dụng máy chủ web cho nội dung tĩnh?
Stephen

4
@Stephen: bởi vì không phải lúc nào cũng có một Apache trước Tomcat / Jetty. Và để tránh những rắc rối của một cấu hình riêng biệt. Nhưng bạn đã đúng, tôi có thể xem xét lựa chọn đó.
Bruno De Fraine

Tôi chỉ không thể hiểu, tại sao bạn không sử dụng ánh xạ như thế này <servlet-maps> <servlet-name> mặc định </ servlet-name> <url-pattern> / </ url-pattern> </ servlet-maps > để phục vụ nội dung tĩnh
Maciek Kreft

Câu trả lời:


53

Tôi đã đưa ra một giải pháp hơi khác nhau. Đó là một chút hack-ish, nhưng đây là ánh xạ:

<servlet-mapping>   
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
 <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>myAppServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Về cơ bản, điều này chỉ ánh xạ tất cả các tệp nội dung bằng phần mở rộng đến servlet mặc định và mọi thứ khác vào "myAppServlet".

Nó hoạt động trong cả Jetty và Tomcat.


13
thực tế bạn có thể thêm nhiều thẻ mô hình url bên trong ánh xạ dịch vụ;)
Fnamed Alnamrouti

5
Servlet 2.5 và mới hơn hỗ trợ nhiều thẻ mẫu url trong ánh xạ servlet
nhóm sinh động

Chỉ cần cẩn thận với các tệp chỉ mục (index.html) vì chúng có thể được ưu tiên hơn so với servlet của bạn.
Andres

Tôi nghĩ đó là ý tưởng tồi sử dụng *.sth. Nếu ai đó sẽ nhận được url example.com/index.jsp?g=.sth, anh ta sẽ lấy nguồn của tệp jsp. Hay tôi nhầm? (Tôi mới sử dụng Java EE) Tôi thường sử dụng mẫu url /css/*và v.v.
SemperPeritus

46

Không cần thực hiện tùy chỉnh hoàn toàn của servlet mặc định trong trường hợp này, bạn có thể sử dụng servlet đơn giản này để bọc yêu cầu cho việc triển khai của trình chứa:


package com.example;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class DefaultWrapperServlet extends HttpServlet
{   
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        RequestDispatcher rd = getServletContext().getNamedDispatcher("default");

        HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
            public String getServletPath() { return ""; }
        };

        rd.forward(wrapped, resp);
    }
}

Câu hỏi này có một cách gọn gàng ánh xạ / tới bộ điều khiển và / tĩnh đến nội dung tĩnh bằng bộ lọc. Kiểm tra câu trả lời nâng cao sau câu trả lời được chấp nhận: stackoverflow.com/questions/870150/
triệt


30

Tôi đã có kết quả tốt với FileServlet , vì nó hỗ trợ khá nhiều tất cả HTTP (etags, chunking, v.v.).


Cảm ơn! hàng giờ cố gắng thất bại và câu trả lời tồi, và điều này đã giải quyết vấn đề của tôi
Yossi Shasho

4
Mặc dù để phục vụ nội dung từ một thư mục bên ngoài ứng dụng (Tôi sử dụng nó để lưu trữ một thư mục từ đĩa, giả sử C: \ resource) Tôi đã sửa đổi hàng này: this.basePath = getServletContext (). GetRealPath (getInitParameter ("basePath ")); Và thay thế nó bằng: this.basePath = getInitParameter ("basePath");
Yossi Shasho

1
Phiên bản cập nhật có sẵn tại showcase.omnifaces.org/servlets/FileServlet
koppor

26

Mẫu trừu tượng cho một servlet tài nguyên tĩnh

Một phần dựa trên blog này từ năm 2007, đây là một mẫu trừu tượng được hiện đại hóa và có thể tái sử dụng cao cho một servlet xử lý đúng cách bộ đệm ETag, If-None-MatchIf-Modified-Since(nhưng không hỗ trợ Gzip và Range; chỉ để đơn giản, Gzip có thể được thực hiện bằng bộ lọc hoặc thông qua cấu hình container).

public abstract class StaticResourceServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private static final long ONE_SECOND_IN_MILLIS = TimeUnit.SECONDS.toMillis(1);
    private static final String ETAG_HEADER = "W/\"%s-%s\"";
    private static final String CONTENT_DISPOSITION_HEADER = "inline;filename=\"%1$s\"; filename*=UTF-8''%1$s";

    public static final long DEFAULT_EXPIRE_TIME_IN_MILLIS = TimeUnit.DAYS.toMillis(30);
    public static final int DEFAULT_STREAM_BUFFER_SIZE = 102400;

    @Override
    protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException ,IOException {
        doRequest(request, response, true);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doRequest(request, response, false);
    }

    private void doRequest(HttpServletRequest request, HttpServletResponse response, boolean head) throws IOException {
        response.reset();
        StaticResource resource;

        try {
            resource = getStaticResource(request);
        }
        catch (IllegalArgumentException e) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }

        if (resource == null) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        String fileName = URLEncoder.encode(resource.getFileName(), StandardCharsets.UTF_8.name());
        boolean notModified = setCacheHeaders(request, response, fileName, resource.getLastModified());

        if (notModified) {
            response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        setContentHeaders(response, fileName, resource.getContentLength());

        if (head) {
            return;
        }

        writeContent(response, resource);
    }

    /**
     * Returns the static resource associated with the given HTTP servlet request. This returns <code>null</code> when
     * the resource does actually not exist. The servlet will then return a HTTP 404 error.
     * @param request The involved HTTP servlet request.
     * @return The static resource associated with the given HTTP servlet request.
     * @throws IllegalArgumentException When the request is mangled in such way that it's not recognizable as a valid
     * static resource request. The servlet will then return a HTTP 400 error.
     */
    protected abstract StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException;

    private boolean setCacheHeaders(HttpServletRequest request, HttpServletResponse response, String fileName, long lastModified) {
        String eTag = String.format(ETAG_HEADER, fileName, lastModified);
        response.setHeader("ETag", eTag);
        response.setDateHeader("Last-Modified", lastModified);
        response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME_IN_MILLIS);
        return notModified(request, eTag, lastModified);
    }

    private boolean notModified(HttpServletRequest request, String eTag, long lastModified) {
        String ifNoneMatch = request.getHeader("If-None-Match");

        if (ifNoneMatch != null) {
            String[] matches = ifNoneMatch.split("\\s*,\\s*");
            Arrays.sort(matches);
            return (Arrays.binarySearch(matches, eTag) > -1 || Arrays.binarySearch(matches, "*") > -1);
        }
        else {
            long ifModifiedSince = request.getDateHeader("If-Modified-Since");
            return (ifModifiedSince + ONE_SECOND_IN_MILLIS > lastModified); // That second is because the header is in seconds, not millis.
        }
    }

    private void setContentHeaders(HttpServletResponse response, String fileName, long contentLength) {
        response.setHeader("Content-Type", getServletContext().getMimeType(fileName));
        response.setHeader("Content-Disposition", String.format(CONTENT_DISPOSITION_HEADER, fileName));

        if (contentLength != -1) {
            response.setHeader("Content-Length", String.valueOf(contentLength));
        }
    }

    private void writeContent(HttpServletResponse response, StaticResource resource) throws IOException {
        try (
            ReadableByteChannel inputChannel = Channels.newChannel(resource.getInputStream());
            WritableByteChannel outputChannel = Channels.newChannel(response.getOutputStream());
        ) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_STREAM_BUFFER_SIZE);
            long size = 0;

            while (inputChannel.read(buffer) != -1) {
                buffer.flip();
                size += outputChannel.write(buffer);
                buffer.clear();
            }

            if (resource.getContentLength() == -1 && !response.isCommitted()) {
                response.setHeader("Content-Length", String.valueOf(size));
            }
        }
    }

}

Sử dụng nó cùng với giao diện bên dưới đại diện cho một tài nguyên tĩnh.

interface StaticResource {

    /**
     * Returns the file name of the resource. This must be unique across all static resources. If any, the file
     * extension will be used to determine the content type being set. If the container doesn't recognize the
     * extension, then you can always register it as <code>&lt;mime-type&gt;</code> in <code>web.xml</code>.
     * @return The file name of the resource.
     */
    public String getFileName();

    /**
     * Returns the last modified timestamp of the resource in milliseconds.
     * @return The last modified timestamp of the resource in milliseconds.
     */
    public long getLastModified();

    /**
     * Returns the content length of the resource. This returns <code>-1</code> if the content length is unknown.
     * In that case, the container will automatically switch to chunked encoding if the response is already
     * committed after streaming. The file download progress may be unknown.
     * @return The content length of the resource.
     */
    public long getContentLength();

    /**
     * Returns the input stream with the content of the resource. This method will be called only once by the
     * servlet, and only when the resource actually needs to be streamed, so lazy loading is not necessary.
     * @return The input stream with the content of the resource.
     * @throws IOException When something fails at I/O level.
     */
    public InputStream getInputStream() throws IOException;

}

Tất cả những gì bạn cần chỉ là mở rộng từ servlet trừu tượng đã cho và thực hiện getStaticResource()phương thức theo javadoc.

Ví dụ cụ thể phục vụ từ hệ thống tập tin:

Đây là một ví dụ cụ thể phục vụ nó thông qua một URL như /files/foo.exttừ hệ thống tệp đĩa cục bộ:

@WebServlet("/files/*")
public class FileSystemResourceServlet extends StaticResourceServlet {

    private File folder;

    @Override
    public void init() throws ServletException {
        folder = new File("/path/to/the/folder");
    }

    @Override
    protected StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException {
        String pathInfo = request.getPathInfo();

        if (pathInfo == null || pathInfo.isEmpty() || "/".equals(pathInfo)) {
            throw new IllegalArgumentException();
        }

        String name = URLDecoder.decode(pathInfo.substring(1), StandardCharsets.UTF_8.name());
        final File file = new File(folder, Paths.get(name).getFileName().toString());

        return !file.exists() ? null : new StaticResource() {
            @Override
            public long getLastModified() {
                return file.lastModified();
            }
            @Override
            public InputStream getInputStream() throws IOException {
                return new FileInputStream(file);
            }
            @Override
            public String getFileName() {
                return file.getName();
            }
            @Override
            public long getContentLength() {
                return file.length();
            }
        };
    }

}

Ví dụ cụ thể phục vụ từ cơ sở dữ liệu:

Đây là một ví dụ cụ thể phục vụ nó thông qua một URL như /files/foo.exttừ cơ sở dữ liệu thông qua một cuộc gọi dịch vụ EJB trả về thực thể của bạn có một thuộc byte[] contenttính:

@WebServlet("/files/*")
public class YourEntityResourceServlet extends StaticResourceServlet {

    @EJB
    private YourEntityService yourEntityService;

    @Override
    protected StaticResource getStaticResource(HttpServletRequest request) throws IllegalArgumentException {
        String pathInfo = request.getPathInfo();

        if (pathInfo == null || pathInfo.isEmpty() || "/".equals(pathInfo)) {
            throw new IllegalArgumentException();
        }

        String name = URLDecoder.decode(pathInfo.substring(1), StandardCharsets.UTF_8.name());
        final YourEntity yourEntity = yourEntityService.getByName(name);

        return (yourEntity == null) ? null : new StaticResource() {
            @Override
            public long getLastModified() {
                return yourEntity.getLastModified();
            }
            @Override
            public InputStream getInputStream() throws IOException {
                return new ByteArrayInputStream(yourEntityService.getContentById(yourEntity.getId()));
            }
            @Override
            public String getFileName() {
                return yourEntity.getName();
            }
            @Override
            public long getContentLength() {
                return yourEntity.getContentLength();
            }
        };
    }

}

1
Kính gửi @BalusC Tôi nghĩ rằng cách tiếp cận của bạn dễ bị tấn công bởi một hacker gửi yêu cầu sau có thể điều hướng hệ thống tệp : files/%2e%2e/mysecretfile.txt. Yêu cầu này tạo ra files/../mysecretfile.txt. Tôi đã thử nó trên Tomcat 7.0.55. Họ gọi nó là một thư mục leo núi: owasp.org/index.php/Path_Traversal
Cristian Arteaga

1
@Cristian: Yup, có thể. Tôi đã cập nhật ví dụ để chỉ ra cách ngăn chặn điều đó.
BalusC

Điều này không nên nhận upvote. Phục vụ các tệp tĩnh cho một trang web với Servlet như thế này là một công thức cho bảo mật thảm họa khôn ngoan. Tất cả các vấn đề như vậy đã được giải quyết, và không có lý do gì để thực hiện một cách Tùy chỉnh mới với nhiều quả bom thời gian bảo mật chưa được phát hiện sẽ phát nổ. Đường dẫn chính xác là định cấu hình Tomcat / GlassFish / Jetty, v.v để phục vụ nội dung, hoặc thậm chí tốt hơn để sử dụng một máy chủ tệp chuyên dụng như NGinX.
Leonhard Printz

@LeonhardPrintz: Tôi sẽ xóa câu trả lời và báo cáo lại cho bạn bè của tôi tại Tomcat sau khi bạn chỉ ra vấn đề bảo mật. Không vấn đề gì.
BalusC

19

Cuối cùng tôi đã tự lăn StaticServlet. Nó hỗ trợ If-Modified-Since, mã hóa gzip và nó cũng có thể phục vụ các tệp tĩnh từ các tệp chiến tranh. Nó không phải là mã rất khó, nhưng nó cũng không hoàn toàn tầm thường.

Mã có sẵn: StaticServlet.java . Hãy bình luận.

Cập nhật: Khurram hỏi về ServletUtilslớp được tham chiếu StaticServlet. Nó chỉ đơn giản là một lớp với các phương thức phụ trợ mà tôi đã sử dụng cho dự án của mình. Phương thức duy nhất bạn cần là coalesce(giống hệt với hàm SQL COALESCE). Đây là mã:

public static <T> T coalesce(T...ts) {
    for(T t: ts)
        if(t != null)
            return t;
    return null;
}

2
Không đặt tên Lỗi lớp bên trong của bạn. Điều đó có thể gây nhầm lẫn vì bạn có thể nhầm nó với java.lang.Error Ngoài ra, webDB của bạn có giống nhau không?
Leonel

Cảm ơn cảnh báo Lỗi. web.xml giống nhau, với "mặc định" được thay thế bằng tên của StaticServlet.
Bruno De Fraine

1
Đối với phương thức hợp nhất, nó có thể được thay thế (bên trong lớp Servlet) bằng commons-lang StringUtils.defaultString (String, String)
Mike Minicki

Phương thức transferStreams () cũng có thể được thay thế bằng Files.copy (is, os);
Gerrit Brink

Tại sao phương pháp này rất phổ biến? Tại sao mọi người lại thực hiện các máy chủ tệp tĩnh như thế này? Có quá nhiều lỗ hổng bảo mật đang chờ được khám phá và rất nhiều tính năng của các máy chủ tệp tĩnh thực sự không được triển khai.
Leonhard Printz

12

Đánh giá từ các thông tin ví dụ ở trên, tôi nghĩ rằng toàn bộ bài viết này dựa trên hành vi có lỗi trong Tomcat 6.0,29 trở về trước. Xem https://issues.apache.org/ormszilla/show_orms.cgi?id=50026 . Nâng cấp lên Tomcat 6.0.30 và hành vi giữa (Tomcat | Jetty) sẽ hợp nhất.


1
Đó cũng là sự hiểu biết của tôi từ svn diff -c1056763 http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk/. Cuối cùng, sau khi đánh dấu WONTFIX +3 năm trước!
Bruno De Fraine

12

thử cái này

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
    <url-pattern>*.css</url-pattern>
    <url-pattern>*.ico</url-pattern>
    <url-pattern>*.png</url-pattern>
    <url-pattern>*.jpg</url-pattern>
    <url-pattern>*.htc</url-pattern>
    <url-pattern>*.gif</url-pattern>
</servlet-mapping>    

Chỉnh sửa: Điều này chỉ hợp lệ cho thông số kỹ thuật của servlet 2.5 trở lên.


Có vẻ đây không phải là một cấu hình hợp lệ.
Gedrox

10

Tôi đã có cùng một vấn đề và tôi đã giải quyết nó bằng cách sử dụng mã của 'servlet mặc định' từ cơ sở mã Tomcat.

https://github.com/apache/tomcat/blob/master/java/org/apache/catalina/servlets/DefaultServlet.java

Các DefaultServlet là servlet phục vụ các tài nguyên tĩnh (jpg, html, css, gif vv) trong Tomcat.

Servlet này rất hiệu quả và có một số thuộc tính bạn đã xác định ở trên.

Tôi nghĩ rằng mã nguồn này, là một cách tốt để bắt đầu và loại bỏ các chức năng hoặc phụ thuộc mà bạn không cần.

  • Các tham chiếu đến gói org.apache.naming.resource có thể được gỡ bỏ hoặc thay thế bằng mã java.io.File.
  • Các tham chiếu đến gói org.apache.cirthina.util chỉ có thể là các phương thức / lớp tiện ích có thể được sao chép trong mã nguồn của bạn.
  • Các tham chiếu đến lớp org.apache.cirthina.Globals có thể được nội tuyến hoặc loại bỏ.

Nó dường như phụ thuộc vào rất nhiều thứ từ org.apache.*. Làm thế nào bạn có thể sử dụng nó với Jetty?
Bruno De Fraine

Bạn nói đúng, phiên bản này có quá nhiều phụ thuộc vào Tomcat (caand nó cũng hỗ trợ nhiều thứ bạn có thể không muốn. Tôi sẽ chỉnh sửa câu trả lời của tôi.
Panagiotis Korros


4

Tôi đã làm điều này bằng cách mở rộng tomcat DefaultServlet ( src ) và ghi đè phương thức getRelativePath ().

package com.example;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.servlets.DefaultServlet;

public class StaticServlet extends DefaultServlet
{
   protected String pathPrefix = "/static";

   public void init(ServletConfig config) throws ServletException
   {
      super.init(config);

      if (config.getInitParameter("pathPrefix") != null)
      {
         pathPrefix = config.getInitParameter("pathPrefix");
      }
   }

   protected String getRelativePath(HttpServletRequest req)
   {
      return pathPrefix + super.getRelativePath(req);
   }
}

... Và đây là ánh xạ servlet của tôi

<servlet>
    <servlet-name>StaticServlet</servlet-name>
    <servlet-class>com.example.StaticServlet</servlet-class>
    <init-param>
        <param-name>pathPrefix</param-name>
        <param-value>/static</param-value>
    </init-param>       
</servlet>

<servlet-mapping>
    <servlet-name>StaticServlet</servlet-name>
    <url-pattern>/static/*</url-pattern>
</servlet-mapping>  

1

Để phục vụ tất cả các yêu cầu từ ứng dụng Spring cũng như /favicon.ico và các tệp tin JSP từ / WEB-INF / jsp / * mà Spring's AbstractUrlBasingView sẽ yêu cầu bạn chỉ cần ánh xạ lại dịch vụ jsp và servlet mặc định:

  <servlet>
    <servlet-name>springapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>/WEB-INF/jsp/*</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/favicon.ico</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>springapp</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

Chúng tôi không thể dựa vào mẫu url * .jsp trên ánh xạ chuẩn cho jsp servlet vì mẫu đường dẫn '/ *' được khớp trước khi bất kỳ ánh xạ mở rộng nào được chọn. Ánh xạ jsp servlet vào một thư mục sâu hơn có nghĩa là nó được khớp trước. Khớp '/favicon.ico' chính xác xảy ra trước khi khớp mẫu đường dẫn. Các kết hợp đường dẫn sâu hơn sẽ hoạt động hoặc khớp chính xác, nhưng không có kết quả khớp mở rộng nào có thể vượt qua khớp đường dẫn '/ *'. Ánh xạ '/' vào servlet mặc định dường như không hoạt động. Bạn sẽ nghĩ chính xác '/' sẽ đánh bại mẫu đường dẫn '/ *' trên springapp.

Giải pháp bộ lọc ở trên không hoạt động đối với các yêu cầu JSP được chuyển tiếp / bao gồm từ ứng dụng. Để làm cho nó hoạt động, tôi phải áp dụng bộ lọc trực tiếp cho springapp, tại thời điểm đó, việc khớp mẫu url là vô ích vì tất cả các yêu cầu đi đến ứng dụng cũng đi đến các bộ lọc của nó. Vì vậy, tôi đã thêm khớp mẫu vào bộ lọc và sau đó tìm hiểu về servlet 'jsp' và thấy rằng nó không xóa tiền tố đường dẫn giống như servlet mặc định. Điều đó đã giải quyết vấn đề của tôi, không hoàn toàn giống nhau nhưng đủ phổ biến.


1

Đã kiểm tra Tomcat 8.x: tài nguyên tĩnh hoạt động tốt nếu bản đồ servlet gốc thành "". Đối với servlet 3.x nó có thể được thực hiện bởi@WebServlet("")


0

Sử dụng org.mortbay.jetty.handler.ContextHandler. Bạn không cần các thành phần bổ sung như StaticServlet.

Tại nhà cầu cảng,

bối cảnh $ cd

$ cp javadoc.xml static.xml

$ vi static.xml

...

<Configure class="org.mortbay.jetty.handler.ContextHandler">
<Set name="contextPath">/static</Set>
<Set name="resourceBase"><SystemProperty name="jetty.home" default="."/>/static/</Set>
<Set name="handler">
  <New class="org.mortbay.jetty.handler.ResourceHandler">
    <Set name="cacheControl">max-age=3600,public</Set>
  </New>
 </Set>
</Configure>

Đặt giá trị của contextPath với tiền tố URL của bạn và đặt giá trị của resourceBase làm đường dẫn tệp của nội dung tĩnh.

Nó làm việc cho tôi.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.