เหตุใด Spring MVC จึงตอบสนองด้วย 404 และรายงาน“ ไม่พบการแมปสำหรับคำขอ HTTP ที่มี URI […] ใน DispatcherServlet”


92

ฉันกำลังเขียนแอปพลิเคชัน Spring MVC ที่ติดตั้งบน Tomcat ดูตัวอย่างขั้นต่ำสมบูรณ์และตรวจสอบได้ต่อไปนี้

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[] { "/*" };
    }
}

ที่ไหน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;
    }
}

สุดท้ายฉันมี@Controllerในแพ็คเกจcom.example.controllers

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

Exampleชื่อบริบทใบสมัครของฉันคือ เมื่อฉันส่งคำขอไปที่

http://localhost:8080/Example/home

แอปพลิเคชันตอบสนองด้วย HTTP Status 404 และบันทึกข้อมูลต่อไปนี้

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'

ฉันมีทรัพยากร JSP ที่/WEB-INF/jsps/index.jspฉันคาดว่า Spring MVC จะใช้คอนโทรลเลอร์ของฉันเพื่อจัดการคำขอและส่งต่อไปยัง JSP เหตุใดจึงตอบสนองด้วย 404


นี่คือโพสต์ตามรูปแบบบัญญัติสำหรับคำถามเกี่ยวกับข้อความเตือนนี้

คำตอบ:


102

แอปพลิเคชัน Spring MVC มาตรฐานของคุณจะตอบสนองคำขอทั้งหมดผ่านทางDispatcherServletที่คุณได้ลงทะเบียนไว้กับคอนเทนเนอร์ Servlet ของคุณ

DispatcherServletรูปลักษณ์ที่ดีApplicationContextและหากมีการApplicationContextลงทะเบียนกับContextLoaderListenerถั่วพิเศษจะต้องมีการติดตั้งขอตรรกะการแสดง ถั่วเหล่านี้จะอธิบายไว้ในเอกสาร

ที่สำคัญที่สุดถั่วประเภทHandlerMappingแผนที่

คำขอที่เข้ามาถึงตัวจัดการและรายชื่อของตัวจัดการก่อนและหลังโปรเซสเซอร์ (ตัวดักจับตัวจัดการ) ตามเกณฑ์บางประการซึ่งรายละเอียดจะแตกต่างกันไปตามHandlerMappingการนำไปใช้ การใช้งานที่ได้รับความนิยมสูงสุดสนับสนุนตัวควบคุมที่มีคำอธิบายประกอบ แต่มีการใช้งานอื่น ๆ

Javadoc ของHandlerMappingต่อไปอธิบายวิธีการใช้งานจะต้องประพฤติ

DispatcherServletพบถั่วประเภทนี้ทั้งหมดและลงทะเบียนไว้ในลำดับที่บางคน (สามารถกำหนดเองได้) ในขณะที่การให้บริการการร้องขอที่DispatcherServletลูปเหล่านี้ผ่านHandlerMappingวัตถุและการทดสอบแต่ละของพวกเขาด้วยที่จะหาคนที่สามารถจัดการกับการร้องขอเข้ามาแสดงเป็นมาตรฐานgetHandler HttpServletRequestตั้งแต่ 4.3.x หากไม่พบสิ่งใดระบบจะบันทึกคำเตือนที่คุณเห็น

ไม่พบการแมปสำหรับคำขอ HTTP ที่มี URI [/some/path]ในDispatcherServletชื่อ SomeName

และทั้งพ่นNoHandlerFoundExceptionหรือทันทีกระทำการตอบสนองด้วยรหัสสถานะ 404 ไม่พบ

เหตุใดจึงไม่DispatcherServletพบสิ่งHandlerMappingที่สามารถจัดการกับคำขอของฉันได้

การHandlerMappingใช้งานที่พบบ่อยที่สุดคือการRequestMappingHandlerMappingจัดการการลงทะเบียน@Controllerbean เป็นตัวจัดการ (จริงๆแล้วเป็น@RequestMappingวิธีการที่มีคำอธิบายประกอบ) คุณสามารถประกาศถั่วชนิดนี้ด้วยตัวเอง (มี@Beanหรือ<bean>หรือกลไกอื่น ๆ ) หรือคุณสามารถใช้ตัวเลือก เหล่านี้คือ:

  1. คำอธิบายของคุณเรียนกับ@Configuration@EnableWebMvc
  2. ประกาศ<mvc:annotation-driven />สมาชิกในการกำหนดค่า XML ของคุณ

ตามที่ลิงค์ด้านบนอธิบายไว้ทั้งสองอย่างนี้จะลงทะเบียนRequestMappingHandlerMappingbean (และของอื่น ๆ อีกมากมาย) อย่างไรก็ตาม a HandlerMappingไม่มีประโยชน์มากหากไม่มีตัวจัดการ RequestMappingHandlerMappingคาดหวัง@Controllerถั่วบางอย่างดังนั้นคุณต้องประกาศสิ่งเหล่านี้ด้วย@Beanวิธีการในการกำหนดค่า Java หรือ<bean>การประกาศในการกำหนดค่า XML หรือผ่านการสแกนส่วนประกอบของ@Controllerคลาสที่มีคำอธิบายประกอบในอย่างใดอย่างหนึ่ง ตรวจสอบให้แน่ใจว่ามีถั่วเหล่านี้อยู่

หากคุณได้รับข้อความเตือนและ 404 และคุณได้กำหนดค่าทั้งหมดข้างต้นอย่างถูกต้องแสดงว่าคุณกำลังส่งคำขอของคุณไปยัง URI ที่ไม่ถูกต้องซึ่งไม่ได้รับการจัดการโดย@RequestMappingวิธีการจัดการคำอธิบายประกอบที่ตรวจพบ

spring-webmvcข้อเสนออื่น ๆ ในห้องสมุดในตัวHandlerMappingการใช้งาน ตัวอย่างเช่นBeanNameUrlHandlerMappingแผนที่

จาก URL ไปจนถึงถั่วที่มีชื่อที่ขึ้นต้นด้วยเครื่องหมายทับ ("/")

และคุณสามารถเขียนของคุณเองได้ตลอดเวลา เห็นได้ชัดว่าคุณจะต้องตรวจสอบให้แน่ใจว่าคำขอที่คุณส่งนั้นตรงกับตัวจัดการHandlerMappingวัตถุที่ลงทะเบียนอย่างน้อยหนึ่งรายการ

ถ้าคุณทำไม่ได้โดยปริยายหรืออย่างชัดเจนลงทะเบียนใด ๆHandlerMappingถั่ว (หรือถ้าdetectAllHandlerMappingsเป็นtrue) ที่DispatcherServletลงทะเบียนบางส่วนเป็นค่าเริ่มต้น สิ่งเหล่านี้ถูกกำหนดไว้ในDispatcherServlet.propertiesแพ็คเกจเดียวกับDispatcherServletคลาส พวกเขาเป็นBeanNameUrlHandlerMappingและDefaultAnnotationHandlerMapping(ซึ่งคล้ายกับRequestMappingHandlerMappingแต่เลิกใช้แล้ว)

การแก้จุดบกพร่อง

ฤดูใบไม้ผลิ MVC RequestMappingHandlerMappingจะเข้าสู่ระบบรถขนลงทะเบียนผ่าน ตัวอย่างเช่นก@Controllerเช่น

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

จะบันทึกสิ่งต่อไปนี้ที่ระดับ INFO

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

สิ่งนี้อธิบายถึงการทำแผนที่ที่ลงทะเบียน เมื่อคุณเห็นคำเตือนว่าไม่พบตัวจัดการให้เปรียบเทียบ URI ในข้อความกับการแมปที่แสดงที่นี่ ข้อ จำกัด ทั้งหมดที่ระบุไว้ใน@RequestMappingต้องตรงกับ Spring MVC เพื่อเลือกตัวจัดการ

อื่น ๆ HandlerMappingการใช้งานบันทึกคำสั่งของตนเองที่ควรบอกใบ้ถึงการแมปและตัวจัดการที่เกี่ยวข้อง

ในทำนองเดียวกันให้เปิดใช้งาน Spring logging ที่ระดับ DEBUG เพื่อดูว่าถั่วใดที่ Spring ลงทะเบียน ควรรายงานคลาสที่มีคำอธิบายประกอบที่พบซึ่งแพ็กเกจที่สแกนและถั่วใดที่เริ่มต้น หากสิ่งที่คุณต้องการไม่ปรากฏให้ตรวจสอบApplicationContextการกำหนดค่าของคุณ

ข้อผิดพลาดทั่วไปอื่น ๆ

A DispatcherServletเป็นเพียง Java EE Servletทั่วไป คุณลงทะเบียนด้วยคำสั่งทั่วไป<web.xml> <servlet-class>และ<servlet-mapping>คำประกาศของคุณหรือโดยตรงผ่านServletContext#addServletใน a WebApplicationInitializerหรือด้วยกลไกใดก็ตามที่ Spring boot ใช้ ดังนั้นคุณต้องอาศัยตรรกะการแมป URL ที่ระบุในข้อกำหนดของ Servletโปรดดูบทที่ 12

ด้วยเหตุนี้ข้อผิดพลาดทั่วไปคือการลงทะเบียนDispatcherServletด้วยการแมป url ของการ/*ส่งคืนชื่อมุมมองจาก@RequestMappingเมธอดตัวจัดการและคาดว่าจะมีการแสดงผล JSP ตัวอย่างเช่นพิจารณาวิธีการจัดการเช่น

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

ด้วย InternalResourceViewResolver

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

คุณอาจคาดหวังการร้องขอที่จะส่งต่อไปให้กับทรัพยากรที่ JSP /WEB-INF/jsps/example-view-name.jspเส้นทาง สิ่งนี้จะไม่เกิดขึ้น แต่สมมติว่าชื่อบริบทของExampleการDisaptcherServletจะรายงาน

ไม่พบการแมปสำหรับคำขอ HTTP ที่มี URI [/Example/WEB-INF/jsps/example-view-name.jsp]ในDispatcherServletชื่อ "dispatcher"

เนื่องจากDispatcherServletมีการแมปกับ/*และ/*ตรงกับทุกอย่าง (ยกเว้นการจับคู่แบบตรงทั้งหมดซึ่งมีลำดับความสำคัญสูงกว่า) จึงDispatcherServletจะถูกเลือกให้จัดการforwardจากJstlView(ส่งคืนโดยInternalResourceViewResolver) ในเกือบทุกกรณีDispatcherServletจะไม่มีการกำหนดค่าให้จัดการคำขอดังกล่าวจะไม่ได้รับการกำหนดค่าการจัดการการร้องขอดังกล่าว

แต่ในกรณีง่ายๆนี้คุณควรลงทะเบียนDispatcherServletto /โดยทำเครื่องหมายว่าเป็น servlet เริ่มต้น servlet เริ่มต้นคือการจับคู่สุดท้ายสำหรับคำขอ สิ่งนี้จะช่วยให้คอนเทนเนอร์ servlet ทั่วไปของคุณสามารถเลือกการใช้งาน Servlet ภายในที่แมป*.jspเพื่อจัดการทรัพยากร JSP (ตัวอย่างเช่น Tomcat มีJspServlet ) ก่อนที่จะลองใช้ servlet เริ่มต้น

นั่นคือสิ่งที่คุณเห็นในตัวอย่างของคุณ


ด้วย @EnableWebMvc dispatcherServlet ได้ลงทะเบียนไปแล้วกับ / "คุณอาจคาดหวังว่าคำขอจะถูกส่งต่อไปยังทรัพยากร JSP ที่พา ธ /WEB-INF/jsps/example-view-name.jsp ซึ่งจะไม่เกิดขึ้น" คุณจะทำให้มันทำงานอย่างไรเพื่อส่งต่อไปยังทรัพยากร JSP ที่เส้นทางนั้น? นั่นเป็นคำถามที่ถามโดยทั่วไป
Tor

@Tor ในคลาสที่@EnableWebMvcมี@Configurationคำอธิบายประกอบจะไม่ทำเช่นนั้น เพียงแค่เพิ่มตัวจัดการ / อะแดปเตอร์ Spring MVC เริ่มต้นจำนวนหนึ่งเข้ากับบริบทของแอปพลิเคชัน การลงทะเบียนDispatcherServletเพื่อให้บริการ/เป็นกระบวนการที่แยกจากกันโดยสิ้นเชิงซึ่งทำในหลายวิธีที่ฉันอธิบายไว้ในส่วนข้อผิดพลาดทั่วไปอื่น ๆ ฉันตอบคำถามที่ถามสองย่อหน้าด้านล่างสิ่งที่คุณยกมา
Sotirios Delimanolis

5

ฉันแก้ไขปัญหาของฉันเมื่อนอกเหนือจากที่อธิบายไว้ก่อนหน้านี้: "

@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: ไฟล์ JSP ไม่แสดงผลในเว็บแอปพลิเคชัน Spring Boot


2

ในกรณีของฉันฉันกำลังติดตามเอกสาร Interceptors Spring สำหรับเวอร์ชัน 5.1.2 (ในขณะที่ใช้Spring Boot v2.0.4.RELEASE ) และWebConfigชั้นเรียนมีคำอธิบายประกอบ@EnableWebMvcซึ่งดูเหมือนจะขัดแย้งกับอย่างอื่นในแอปพลิเคชันของฉันที่ป้องกันไม่ให้คงที่ของฉัน เนื้อหาไม่ได้รับการแก้ไขอย่างถูกต้อง (กล่าวคือไม่มีการส่งคืนไฟล์ CSS หรือ JS ไปยังไคลเอนต์)

หลังจากพยายามมากของสิ่งที่แตกต่างกันฉันพยายามลบ@EnableWebMvcและจะทำงาน!

แก้ไข: นี่คือเอกสารอ้างอิงที่ระบุว่าคุณควรลบ@EnableWebMvcคำอธิบายประกอบ

เห็นได้ชัดว่าในกรณีของฉันอย่างน้อยฉันได้กำหนดค่าแอปพลิเคชัน Spring ของฉันแล้ว (แม้ว่าจะไม่ได้ใช้web.xmlหรือไฟล์คงที่อื่น ๆ แต่ก็เป็นแบบทางโปรแกรมอย่างแน่นอน) ดังนั้นจึงมีข้อขัดแย้งอยู่ที่นั่น


1

พยายามแก้ไขรหัสของคุณด้วยการเปลี่ยนแปลงต่อไปนี้ในไฟล์กำหนดค่าของคุณ ใช้การกำหนดค่า Java แทนapplication.properties. อย่าลืมเปิดใช้งานการกำหนดค่าในconfigureDefaultServletHandlingวิธีการ

WebMvcConfigurerAdapterคลาสเลิกใช้แล้วดังนั้นเราจึงใช้WebMvcConfigurerอินเทอร์เฟซ

@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();
    }
}

ฉันใช้ gradle คุณควรมีการอ้างอิงต่อไปนี้ใน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

ฉันเจออีกสาเหตุหนึ่งของข้อผิดพลาดเดียวกัน อาจเป็นเพราะไฟล์คลาสไม่ได้สร้างขึ้นสำหรับไฟล์ controller.java ของคุณ อันเป็นผลมาจากการที่ servlet ตัวเลือกจ่ายงานที่กล่าวถึงใน web.xml ไม่สามารถแมปกับเมธอดที่เหมาะสมในคลาสคอนโทรลเลอร์

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

ใน eclipse ภายใต้ Project-> เลือก clean -> Build Project ตรวจสอบว่าไฟล์คลาสถูกสร้างขึ้นสำหรับไฟล์คอนโทรลเลอร์ภายใต้บิลด์ในเวิร์กสเปซของคุณหรือไม่


0

สำหรับฉันฉันพบว่าคลาสเป้าหมายของฉันถูกสร้างขึ้นในรูปแบบโฟลเดอร์ที่ไม่เหมือนกับซอร์ส นี่อาจเป็นไปได้ใน eclipse ฉันเพิ่มโฟลเดอร์สำหรับมีคอนโทรลเลอร์ของฉันและไม่เพิ่มเป็นแพ็คเกจ ดังนั้นฉันจึงกำหนดเส้นทางที่ไม่ถูกต้องในการกำหนดค่าสปริง

คลาสเป้าหมายของฉันกำลังสร้างคลาสภายใต้แอพและฉันอ้างถึง com.happy.app

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

ฉันเพิ่มแพ็กเกจ (ไม่ใช่โฟลเดอร์) สำหรับ com.happy.app และย้ายไฟล์จากโฟลเดอร์ไปยังแพ็กเกจใน eclipse และแก้ไขปัญหาได้


0

ทำความสะอาดเซิร์ฟเวอร์ของคุณ อาจลบเซิร์ฟเวอร์และเพิ่มโครงการอีกครั้งและเรียกใช้

  1. หยุดเซิร์ฟเวอร์ Tomcat

  2. คลิกขวาที่เซิร์ฟเวอร์และเลือก "Clean"

  3. คลิกขวาที่เซิร์ฟเวอร์อีกครั้งแล้วเลือก "Clean Tomcat Work Directory"


0

ในกรณีของฉันฉันกำลังเล่นกับการนำเข้าไฟล์ config java รองลงในไฟล์ config java หลัก ในขณะที่สร้างไฟล์กำหนดค่ารองฉันได้เปลี่ยนชื่อของคลาส config หลัก แต่ฉันไม่สามารถอัปเดตชื่อใน web.xml ดังนั้นทุกครั้งที่ฉันรีสตาร์ทเซิร์ฟเวอร์ tomcat ฉันไม่เห็นตัวจัดการการแมปที่ระบุไว้ในคอนโซล Eclipse IDE และเมื่อฉันพยายามไปที่โฮมเพจของฉันฉันพบข้อผิดพลาดนี้:

1 พ.ย. 2562 23:00:01 น. org.springframework.web.servlet.PageNotFound noHandlerFound คำเตือน: ไม่พบการแมปสำหรับคำขอ HTTP ที่มี URI [/ webapp / home / index] ใน DispatcherServlet ที่มีชื่อ 'dispatcher'

การแก้ไขคือการอัปเดตไฟล์ web.xml เพื่อให้ชื่อเก่า "WebConfig" เป็น "MainConfig" แทนเพียงแค่เปลี่ยนชื่อให้แสดงชื่อล่าสุดของไฟล์ config java หลัก (โดยที่ "MainConfig" เป็นคำที่กำหนดเองและคำว่า " เว็บ "และ" หลัก "ที่ใช้ที่นี่ไม่ใช่ข้อกำหนดทางไวยากรณ์) MainConfig มีความสำคัญเนื่องจากเป็นไฟล์ที่สแกนคอมโพเนนต์สำหรับ "WebController" คลาสคอนโทรลเลอร์ mvc ฤดูใบไม้ผลิของฉันที่จัดการคำขอเว็บของฉัน

@ComponentScan(basePackageClasses={WebController.class})

web.xml มีสิ่งนี้:

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

ขณะนี้ไฟล์ web.xml มี:

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

ตอนนี้ฉันเห็นการแมปในหน้าต่างคอนโซล:

INFO: แมป "{[/ home / index], method = [GET]}" ไปยัง public.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex ()

และหน้าเว็บของฉันกำลังโหลดอีกครั้ง


-1

ฉันมีปัญหาเดียวกันกับ **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

หลังจากที่ฉันวิเคราะห์เป็นเวลา 2 ถึง 4 วันฉันก็พบสาเหตุที่แท้จริง ไฟล์คลาสไม่ได้ถูกสร้างขึ้นหลังจากที่ฉันรันโปรเจ็กต์ ฉันคลิกแท็บโครงการ

Project -> CloseProject -> OpenProject -> Clean -> Build project

สร้างไฟล์คลาสสำหรับซอร์สโค้ดแล้ว มันช่วยแก้ปัญหาของฉันได้ หากต้องการตรวจสอบว่ามีการสร้างไฟล์คลาสหรือไม่โปรดตรวจสอบโฟลเดอร์ Build ในโฟลเดอร์โครงการของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.