Tại sao Spring MVC phản hồi với 404 và báo cáo “Không tìm thấy ánh xạ nào cho yêu cầu HTTP với URI […] trong DispatcherServlet”?


91

Tôi đang viết một ứng dụng Spring MVC được triển khai trên Tomcat. Xem ví dụ tối thiểu, đầy đủ và có thể kiểm chứng sau đây

public class Application extends AbstractAnnotationConfigDispatcherServletInitializer {
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { };
    }
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { SpringServletConfig.class };
    }
    protected String[] getServletMappings() {
        return new String[] { "/*" };
    }
}

Trong trường hợp SpringServletConfig

@Configuration
@ComponentScan("com.example.controllers")
@EnableWebMvc
public class SpringServletConfig {
    @Bean
    public InternalResourceViewResolver resolver() {
        InternalResourceViewResolver vr = new InternalResourceViewResolver();
        vr.setPrefix("/WEB-INF/jsps/");
        vr.setSuffix(".jsp");
        return vr;
    }
}

Cuối cùng, tôi có một @Controllertrong góicom.example.controllers

@Controller
public class ExampleController {
    @RequestMapping(path = "/home", method = RequestMethod.GET)
    public String example() {
        return "index";
    }
}

Tên ngữ cảnh của ứng dụng của tôi là Example. Khi tôi gửi một yêu cầu đến

http://localhost:8080/Example/home

ứng dụng phản hồi với Trạng thái HTTP 404 và ghi lại những điều sau

WARN  o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI `[/Example/WEB-INF/jsps/index.jsp]` in `DispatcherServlet` with name 'dispatcher'

Tôi có một tài nguyên JSP tại /WEB-INF/jsps/index.jspTôi mong đợi Spring MVC sử dụng bộ điều khiển của tôi để xử lý yêu cầu và chuyển tiếp đến JSP, vậy tại sao nó lại phản hồi với 404?


Đây có nghĩa là một bài đăng chuẩn cho các câu hỏi về thông báo cảnh báo này.

Câu trả lời:


100

Ứng dụng Spring MVC tiêu chuẩn của bạn sẽ phục vụ tất cả các yêu cầu thông qua một DispatcherServletmà bạn đã đăng ký với vùng chứa Servlet của mình.

Cái DispatcherServletnhìn vào nó ApplicationContextvà, nếu có, được ApplicationContextđăng ký với một ContextLoaderListenerhạt đậu đặc biệt, nó cần thiết lập logic phục vụ yêu cầu của nó. Những hạt đậu này được mô tả trong tài liệu .

Được cho là quan trọng nhất, đậu của loại HandlerMappingbản đồ

các yêu cầu đến trình xử lý và danh sách các trình xử lý trước và sau (trình đánh chặn trình xử lý) dựa trên một số tiêu chí, chi tiết của chúng khác nhau tùy theo cách HandlerMappingtriển khai. Việc triển khai phổ biến nhất hỗ trợ các bộ điều khiển được chú thích nhưng các triển khai khác cũng tồn tại.

Các javadoc củaHandlerMapping thêm mô tả cách triển khai phải cư xử.

Tìm DispatcherServletthấy tất cả các loại đậu thuộc loại này và đăng ký chúng theo một số thứ tự (có thể được tùy chỉnh). Trong khi phục vụ một yêu cầu, các DispatcherServletvòng lặp qua các HandlerMappingđối tượng này và kiểm tra từng đối tượng getHandlerđể tìm một đối tượng có thể xử lý yêu cầu đến, được biểu thị dưới dạng tiêu chuẩn HttpServletRequest. Kể từ 4.3.x, nếu nó không tìm thấy bất kỳ , nó sẽ ghi lại cảnh báo mà bạn thấy

Không có ánh xạ tìm thấy cho yêu cầu HTTP URI [/some/path]trong DispatcherServletvới tên somename

một trong hai ném một NoHandlerFoundExceptionhoặc ngay lập tức cam kết đáp ứng với một mã trạng thái 404 Not Found.

Tại sao không DispatcherServlettìm thấy một HandlerMappingcó thể xử lý yêu cầu của tôi?

Cách HandlerMappingtriển khai phổ biến nhất là RequestMappingHandlerMappingxử lý việc đăng ký @Controllerbean dưới dạng trình xử lý (thực sự là các @RequestMappingphương thức được chú thích của chúng ). Bạn có thể tự khai báo bean loại này (với @Beanhoặc <bean>hoặc cơ chế khác) hoặc bạn có thể sử dụng các tùy chọn tích hợp sẵn . Đó là:

  1. Chú thích @Configurationlớp của bạn với @EnableWebMvc.
  2. Khai báo một <mvc:annotation-driven />thành viên trong cấu hình XML của bạn.

Như liên kết ở trên mô tả, cả hai thứ này sẽ đăng ký một RequestMappingHandlerMappingbean (và một loạt các thứ khác). Tuy nhiên, một HandlerMappingkhông hữu ích lắm nếu không có một trình xử lý. RequestMappingHandlerMappingmong đợi một số @Controllerbean, vì vậy bạn cũng cần phải khai báo chúng, thông qua @Beancác phương thức trong cấu hình Java hoặc <bean>khai báo trong cấu hình XML hoặc thông qua quét thành phần của @Controllercác lớp được chú thích trong một trong hai. Đảm bảo có những hạt đậu này.

Nếu bạn nhận được thông báo cảnh báo và mã 404 và bạn đã định cấu hình tất cả các điều trên một cách chính xác, thì bạn đang gửi yêu cầu của mình đến sai URI , một URI không được xử lý bằng @RequestMappingphương pháp xử lý có chú thích được phát hiện .

Các spring-webmvcMời thư viện khác được xây dựng trong HandlerMappingviệc triển khai. Ví dụ, BeanNameUrlHandlerMappingbản đồ

từ URL đến bean có tên bắt đầu bằng dấu gạch chéo ("/")

và bạn luôn có thể viết của riêng bạn. Rõ ràng, bạn sẽ phải đảm bảo rằng yêu cầu bạn đang gửi khớp với ít nhất một trong các HandlerMappingtrình xử lý của đối tượng đã đăng ký .

Nếu bạn không đăng ký ngầm định hoặc rõ ràng bất kỳ HandlerMappingbean nào (hoặc nếu detectAllHandlerMappingstrue), thì DispatcherServletđăng ký một số giá trị mặc định . Chúng được định nghĩa trong DispatcherServlet.propertiescùng một gói với DispatcherServletlớp. Chúng là BeanNameUrlHandlerMappingDefaultAnnotationHandlerMapping(tương tự như RequestMappingHandlerMappingnhưng không được dùng nữa).

Gỡ lỗi

Spring MVC sẽ ghi lại các trình xử lý đã đăng ký thông qua RequestMappingHandlerMapping. Ví dụ, một @Controllerlike

@Controller
public class ExampleController {
    @RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
    public String example() {
        return "example-view-name";
    }
}

sẽ ghi lại những điều sau ở cấp độ INFO

Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()

Điều này mô tả ánh xạ đã đăng ký. Khi bạn thấy cảnh báo rằng không tìm thấy trình xử lý nào, hãy so sánh URI trong thông báo với ánh xạ được liệt kê ở đây. Tất cả các giới hạn được chỉ định trong @RequestMappingphải khớp với Spring MVC để chọn trình xử lý.

Các HandlerMappingtriển khai khác ghi lại các câu lệnh của riêng chúng mà sẽ gợi ý cho các ánh xạ của chúng và các trình xử lý tương ứng của chúng.

Tương tự, hãy kích hoạt tính năng ghi nhật ký Spring ở mức GỠ LỖI để xem Spring đăng ký đậu nào. Nó sẽ báo cáo lớp được chú thích nào mà nó tìm thấy, gói nào nó quét và đậu nó khởi tạo. Nếu những cái bạn mong đợi không có, hãy xem lại ApplicationContextcấu hình của bạn .

Những lỗi phổ biến khác

A DispatcherServletchỉ là một Java EE điển hình Servlet. Bạn đăng ký nó với thông thường <web.xml> <servlet-class><servlet-mapping>khai báo của bạn , hoặc trực tiếp thông qua ServletContext#addServletmột WebApplicationInitializerhoặc với bất kỳ cơ chế nào mà Spring boot sử dụng. Như vậy, bạn phải dựa vào logic ánh xạ url được chỉ định trong đặc tả Servlet , xem Chương 12. Xem thêm

Với ý nghĩ đó, một sai lầm phổ biến là đăng ký DispatcherServletánh xạ url của /*, trả về tên chế độ xem từ @RequestMappingphương thức xử lý và mong đợi một JSP được hiển thị. Ví dụ: hãy xem xét một phương thức xử lý như

@RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
    return "example-view-name";
}

với một InternalResourceViewResolver

@Bean
public InternalResourceViewResolver resolver() {
    InternalResourceViewResolver vr = new InternalResourceViewResolver();
    vr.setPrefix("/WEB-INF/jsps/");
    vr.setSuffix(".jsp");
    return vr;
}

bạn có thể mong đợi yêu cầu được chuyển tiếp đến tài nguyên JSP tại đường dẫn /WEB-INF/jsps/example-view-name.jsp. Điều này sẽ không xảy ra. Thay vào đó, giả sử có tên ngữ cảnh là Example, DisaptcherServletsẽ báo cáo

Không có ánh xạ tìm thấy cho HTTP yêu cầu với URI [/Example/WEB-INF/jsps/example-view-name.jsp]trong DispatcherServletvới tên 'phối'

Bởi vì dấu DispatcherServletđược ánh xạ tới /*/*khớp với mọi thứ (ngoại trừ các kết quả chính xác, có mức độ ưu tiên cao hơn), nên DispatcherServletsẽ được chọn để xử lý forwardtừ JstlView(được trả về bởi InternalResourceViewResolver). Trong hầu hết mọi trường hợp, DispatcherServletsẽ không được cấu hình để xử lý một yêu cầu như vậy .

Thay vào đó, trong trường hợp đơn giản này, bạn nên đăng ký DispatcherServlettới /, đánh dấu nó là servlet mặc định. Servlet mặc định là kết quả phù hợp cuối cùng cho một yêu cầu. Điều này sẽ cho phép vùng chứa servlet điển hình của bạn chọn một triển khai Servlet nội bộ, được ánh xạ tới *.jsp, để xử lý tài nguyên JSP (ví dụ: Tomcat có JspServlet), trước khi thử với servlet mặc định.

Đó là những gì bạn đang thấy trong ví dụ của mình.


Với @EnableWebMvc, dispatcherServlet nó đã được đăng ký với /. "bạn có thể mong đợi yêu cầu được chuyển tiếp đến tài nguyên JSP tại đường dẫn /WEB-INF/jsps/example-view-name.jsp. Điều này sẽ không xảy ra." Làm cách nào để bạn làm cho nó hoạt động để nó chuyển tiếp đến tài nguyên JSP tại đường dẫn đó? Về cơ bản đó là câu hỏi được đặt ra.
Tor

@Tor Riêng, @EnableWebMvctrên một @Configurationlớp được chú thích không làm điều đó. Tất cả những gì nó làm là thêm một số trình xử lý / bộ điều hợp Spring MVC mặc định vào ngữ cảnh ứng dụng. Đăng ký DispatcherServletphân phối /là một quy trình hoàn toàn riêng biệt được thực hiện theo một số cách mà tôi mô tả trong phần Các lỗi phổ biến khác . Tôi trả lời câu hỏi hỏi hai đoạn dưới đây những gì bạn đã trích dẫn.
Sotirios Delimanolis

5

Tôi đã giải quyết vấn đề của mình khi ngoài những gì đã mô tả trước đây: '

@Bean
public InternalResourceViewResolver resolver() {
    InternalResourceViewResolver vr = new InternalResourceViewResolver();
    vr.setPrefix("/WEB-INF/jsps/");
    vr.setSuffix(".jsp");
    return vr;
}

added tomcat-embed-jasper:

<dependency>
       <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
       <scope>provided</scope>
</dependency>

`from: Tệp JSP không hiển thị trong ứng dụng web Spring Boot


2

Trong trường hợp của tôi, tôi đang theo dõi tài liệu Interceptors Spring cho phiên bản 5.1.2 (trong khi sử dụng Spring Boot v2.0.4.RELEASE ) và WebConfiglớp có chú thích @EnableWebMvc, có vẻ như xung đột với một cái gì đó khác trong ứng dụng của tôi đang ngăn chặn tĩnh của tôi nội dung không được giải quyết đúng cách (tức là không có tệp CSS hoặc JS nào được trả lại cho máy khách).

Sau khi thử rất nhiều thứ khác nhau, tôi đã cố gắng loại bỏ các @EnableWebMvcvà nó làm việc!

Chỉnh sửa: Đây là tài liệu tham khảo cho biết bạn nên xóa @EnableWebMvcchú thích

Rõ ràng trong trường hợp của tôi ít nhất, tôi đã định cấu hình ứng dụng Spring của mình (mặc dù không phải bằng cách sử dụng web.xmlhoặc bất kỳ tệp tĩnh nào khác, nó chắc chắn theo lập trình), vì vậy nó đã xảy ra xung đột ở đó.


1

Cố gắng sửa đổi mã của bạn với thay đổi sau trên tệp cấu hình của bạn. Cấu hình Java được sử dụng thay vì application.properties. Đừng quên kích hoạt cấu hình trong configureDefaultServletHandlingphương thức.

WebMvcConfigurerAdapterlớp không được dùng nữa, vì vậy chúng tôi sử dụng WebMvcConfigurergiao diện.

@Configuration
@EnableWebMvc
@ComponentScan
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

Tôi sử dụng gradle, bạn nên có các phụ thuộc sau trong pom.xml:

dependencies {

    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.0.RELEASE'
    compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.35'
}

0

Tôi đã gặp một lý do khác cho cùng một lỗi. Điều này cũng có thể là do các tệp lớp không được tạo cho tệp controller.java của bạn. Do đó, servlet điều phối được đề cập trong web.xml không thể ánh xạ nó tới phương thức thích hợp trong lớp bộ điều khiển.

@Controller
Class Controller{
@RequestMapping(value="/abc.html")//abc is the requesting page
public void method()
{.....}
}

Trong eclipse dưới Project-> chọn clean -> Build Project. Hãy kiểm tra xem tệp lớp đã được tạo cho tệp bộ điều khiển dưới các bản dựng trong không gian làm việc của bạn chưa.


0

Đối với tôi, tôi thấy rằng các lớp mục tiêu của tôi được tạo trong một mẫu thư mục không giống với nguồn. Điều này có thể trong nhật thực, tôi thêm các thư mục để chứa bộ điều khiển của mình và không thêm chúng dưới dạng gói. Vì vậy, tôi đã kết thúc việc xác định đường dẫn không chính xác trong cấu hình mùa xuân.

Lớp mục tiêu của tôi đang tạo các lớp trong ứng dụng và tôi đang đề cập đến com.happy.app

<context:annotation-config />
<context:component-scan
    base-package="com.happy.app"></context:component-scan> 

Tôi đã thêm gói (không phải thư mục) cho com.happy.app và chuyển tệp từ thư mục sang gói trong eclipse và nó đã giải quyết được sự cố.


0

Làm sạch máy chủ của bạn. Có thể xóa máy chủ và thêm dự án một lần nữa và Chạy.

  1. Dừng máy chủ Tomcat

  2. Nhấp chuột phải vào máy chủ và chọn "Clean"

  3. Nhấp chuột phải vào máy chủ một lần nữa và chọn "Clean Tomcat Work Directory"


0

Trong trường hợp của tôi, tôi đã thử nhập các tệp cấu hình java phụ vào tệp cấu hình java chính. Trong khi tạo tệp cấu hình phụ, tôi đã thay đổi tên của lớp cấu hình chính, nhưng tôi không cập nhật được tên trong web.xml. Vì vậy, mỗi khi tôi khởi động lại máy chủ tomcat của mình, tôi không thấy trình xử lý ánh xạ được ghi chú trong bảng điều khiển IDE Eclipse và khi tôi cố gắng điều hướng đến trang chủ của mình, tôi đã gặp lỗi này:

Ngày 1 tháng 11 năm 2019 11:00:01 PM org.springframework.web.servlet.PageNotFound noHandlerFound CẢNH BÁO: Không tìm thấy ánh xạ cho yêu cầu HTTP với URI [/ webapp / home / index] trong DispatcherServlet với tên 'điều phối viên'

Cách khắc phục là cập nhật tệp web.xml để tên cũ "WebConfig" sẽ là "MainConfig", chỉ cần đổi tên nó để phản ánh tên mới nhất của tệp cấu hình java chính (trong đó "MainConfig" là tùy ý và các từ " Web "và" Main "được sử dụng ở đây không phải là một yêu cầu về cú pháp). MainConfig rất quan trọng vì nó là tệp đã quét thành phần cho "WebController", lớp bộ điều khiển mvc mùa xuân xử lý các yêu cầu web của tôi.

@ComponentScan(basePackageClasses={WebController.class})

web.xml có cái này:

<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        com.lionheart.fourthed.config.WebConfig
    </param-value>
</init-param>

tệp web.xml hiện có:

<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        com.lionheart.fourthed.config.MainConfig
    </param-value>
</init-param>

Bây giờ tôi đang thấy ánh xạ trong cửa sổ bảng điều khiển:

THÔNG TIN: Đã ánh xạ "{[/ home / index], methods = [GET]}" lên public org.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex ()

Và trang web của tôi đang tải lại.


-1

Tôi đã gặp vấn đề tương tự như **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

Sau khi tôi phân tích từ 2 đến 4 ngày, tôi đã tìm ra nguyên nhân gốc rễ. Các tệp lớp không được tạo sau khi tôi chạy dự án. Tôi đã nhấp vào tab dự án.

Dự án -> CloseProject -> OpenProject -> Clean -> Build project

Các tệp lớp cho mã nguồn đã được tạo. Nó đã giải quyết vấn đề của tôi. Để kiểm tra xem các tệp lớp đã được tạo hay chưa, Vui lòng kiểm tra thư mục Xây dựng trong thư mục dự án của bạn.

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.