25

基本的な構成ファイルは直感的ではありません。

簡単な Hello World の例を作成し、名前を変更home.jspしてからファイルhome.htmlを編集するとします。servlet-context.xml

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean> 

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".html" />
</beans:bean>

エラーが出始めました

WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/myapp/WEB-INF/views/home.html] in DispatcherServlet with name 'appServlet'

なんで?suffixプロパティとは何を意味しますか?

アップデート

私のコントローラーは次のとおりです。ご覧のとおり、ファイル拡張子は含まれていません

@Controller
public class HomeController {

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    /**
     * Simply selects the home view to render by returning its name.
     */
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);

        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

        String formattedDate = dateFormat.format(date);

        model.addAttribute("serverTime", formattedDate );

        return "home";
    }

}
4

7 に答える 7

32

問題の背景

最初に理解しておくべきことは次のとおりです。jsp ファイルをレンダリングするのはスプリングではありません。それを行うのが JspServlet (org.apache.jasper.servlet.JspServlet) です。このサーブレットには、Spring ではなく Tomcat (jasper コンパイラ) が付属しています。この JspServlet は、jsp ページをコンパイルする方法と、それを HTML テキストとしてクライアントに返す方法を認識しています。デフォルトでは、Tomcat の JspServlet は、*.jsp と *.jspx の 2 つのパターンに一致するリクエストのみを処理します。

spring がInternalResourceView(またはJstlView) を使用してビューをレンダリングすると、次の 3 つのことが実際に行われます。

  1. モデルからすべてのモデル パラメーターを取得します (コントローラー ハンドラー メソッドによって返されます"public ModelAndView doSomething() { return new ModelAndView("home") }") 。
  2. これらのモデル パラメータをリクエスト属性として公開します (JspServlet で読み取れるようにするため)。
  3. リクエストを JspServlet に転送します。RequestDispatcher各 *.jsp リクエストを JspServlet に転送する必要があることを認識します (これがデフォルトの tomcat の構成であるため)

ビュー名を単に home.html に変更すると、Tomcat はリクエストの処理方法を認識しません。これは、*.html 要求を処理するサーブレットがないためです。

解決

これを解決する方法。最も明白な解決策は次の 3 つです。

  1. html をリソース ファイルとして公開する
  2. *.html リクエストも処理するように JspServlet に指示する
  3. 独自のサーブレットを作成します (または、*.html への要求を別の既存のサーブレットに渡します)。

初期設定(jsp扱いのみ)

まず、xml ファイルなしで spring を構成すると仮定しましょう (@Configuration アノテーションと spring の WebApplicationInitializer インターフェースのみに基づく)。

基本的な構成は次のとおりです

public class MyWebApplicationContext extends AnnotationConfigWebApplicationContext {
  private static final String CONFIG_FILES_LOCATION = "my.application.root.config";

  public MyWebApplicationContext() {
    super();
    setConfigLocation(CONFIG_FILES_LOCATION);
  }

}

public class AppInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    WebApplicationContext context = new MyWebApplicationContext();
    servletContext.addListener(new ContextLoaderListener(context));

    addSpringDispatcherServlet(servletContext, context);

  }

  private void addSpringDispatcherServlet(ServletContext servletContext, WebApplicationContext context) {
    ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",
      new DispatcherServlet(context));
    dispatcher.setLoadOnStartup(2);
    dispatcher.addMapping("/");
    dispatcher.setInitParameter("throwExceptionIfNoHandlerFound", "true");
  }
}

package my.application.root.config
// (...)

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  @Qualifier("jstlViewResolver")
  private ViewResolver jstlViewResolver;

  @Bean
  @DependsOn({ "jstlViewResolver" })
  public ViewResolver viewResolver() {
    return jstlViewResolver;
  }

  @Bean(name = "jstlViewResolver")
  public ViewResolver jstlViewResolver() {
    UrlBasedViewResolver resolver = new UrlBasedViewResolver();
    resolver.setPrefix("/WEB-INF/internal/");
    resolver.setViewClass(JstlView.class);
    resolver.setSuffix(".jsp");
    return resolver;
  }

}

上記の例では、バッキング ビュー クラス JstlView で UrlBasedViewResolver を使用していますが、例のように InternalResourceViewResolver を使用できます。

上記の例では、末尾が.jsp. 注: 冒頭で述べたように、JstlView は実際には tomcat の RequestDispatcher を使用してリクエストを JspSevlet に転送し、jsp を html にコンパイルします。

ソリューション 1 の実装- html をリソース ファイルとして公開します。

WebConfig クラスを変更して、一致する新しいリソースを追加します。また、jstlViewResolver を変更して、接頭辞も接尾辞も取らないようにする必要があります。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  @Qualifier("jstlViewResolver")
  private ViewResolver jstlViewResolver;

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/someurl/resources/**").addResourceLocations("/resources/");

  }

  @Bean
  @DependsOn({ "jstlViewResolver" })
  public ViewResolver viewResolver() {
    return jstlViewResolver;
  }

  @Bean(name = "jstlViewResolver")
  public ViewResolver jstlViewResolver() {
    UrlBasedViewResolver resolver = new UrlBasedViewResolver();
    resolver.setPrefix(""); // NOTE: no prefix here
    resolver.setViewClass(JstlView.class);
    resolver.setSuffix(""); // NOTE: no suffix here
    return resolver;
  }

// NOTE: you can use InternalResourceViewResolver it does not matter 
//  @Bean(name = "internalResolver")
//  public ViewResolver internalViewResolver() {
//    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
//    resolver.setPrefix("");
//    resolver.setSuffix("");
//    return resolver;
//  }
}

これを追加することで、 http://my.server/someurl/resources/に送られるすべてのリクエストが、Web ディレクトリの下の resources ディレクトリにマップされると言います。したがって、home.html をリソース ディレクトリに置き、ブラウザでhttp://my.server/someurl/resources/home.htmlを指定すると、ファイルが提供されます。コントローラーでこれを処理するには、リソースへのフル パスを返します。

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/someurl/resources/home.html"); // NOTE here there is /someurl/resources
    }

}

同じディレクトリにいくつかの jsp ファイル (*.html ファイルだけでなく) を配置する場合、たとえば同じリソース ディレクトリにある home_dynamic.jsp を同様の方法でアクセスできますが、サーバー上の実際のパスを使用する必要があります。これは .html で終わる html リソースのみのマッピングであるため、パスは/someurl/ で始まりません)。このコンテキストでは、jsp は動的リソースであり、最終的にディスク上の実際のパスを使用して JspServlet によってアクセスされます。したがって、jsp にアクセスする正しい方法は次のとおりです。

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home_dynamic.jsp"); // NOTE here there is /resources (there is no /someurl/ because "someurl" is only for static resources 

}

xml ベースの構成でこれを実現するには、次を使用する必要があります。

<mvc:resources mapping="/someurl/resources/**" location="/resources/" />

jstl ビュー リゾルバーを変更します。

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- Please NOTE that it does not matter if you use InternalResourceViewResolver or UrlBasedViewResolver as in annotations example -->
    <beans:property name="prefix" value="" />
    <beans:property name="suffix" value="" />
</beans:bean>

ソリューション 2 の実装:

このオプションでは、Tomcat の JspServlet を使用して静的ファイルも処理します。結果として、html ファイルで jsp タグを使用できます:) もちろん、使用するかどうかはあなたの選択です。ほとんどの場合、プレーンな html を使用したいので、単純に jsp タグを使用しないでください。コンテンツは静的な html であるかのように提供されます。

最初に、前の例のように、ビュー リゾルバーのプレフィックスとサフィックスを削除します。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  @Qualifier("jstlViewResolver")
  private ViewResolver jstlViewResolver;

  @Bean
  @DependsOn({ "jstlViewResolver" })
  public ViewResolver viewResolver() {
    return jstlViewResolver;
  }

  @Bean(name = "jstlViewResolver")
  public ViewResolver jstlViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver(); // NOTE: this time I'm using InternalResourceViewResolver and again it does not matter :)
    resolver.setPrefix("");
    resolver.setSuffix("");
    return resolver;
  }

}

ここで、*.html ファイルも処理するために JspServlet を追加します。

public class AppInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    WebApplicationContext context = new MyWebApplicationContext();
    servletContext.addListener(new ContextLoaderListener(context));

    addStaticHtmlFilesHandlingServlet(servletContext);
    addSpringDispatcherServlet(servletContext, context);

  }

 // (...)

  private void addStaticHtmlFilesHandlingServlet(ServletContext servletContext) {
    ServletRegistration.Dynamic servlet = servletContext.addServlet("HtmlsServlet", new JspServlet()); // org.apache.jasper.servlet.JspServlet
    servlet.setLoadOnStartup(1);
    servlet.addMapping("*.html");
  }

}

重要なのは、このクラスを使用可能にするには、コンパイル時だけに tomcat のインストールから jasper.jar を追加する必要があることです。Maven アプリをお持ちの場合、jar に提供されている scope= を使用すると、これは非常に簡単です。Maven の依存関係は次のようになります。

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jasper</artifactId>
    <version>${tomcat.libs.version}</version>
    <scope>provided</scope> <!--- NOTE: scope provided! -->
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jsp-api</artifactId>
    <version>${tomcat.libs.version}</version>
    <scope>provided</scope>
</dependency>

あなたがxmlの方法でそれをしたい場合。*.html リクエストを処理するには、jsp サーブレットを登録する必要があるため、次のエントリを web.xml に追加する必要があります。

<servlet>
    <servlet-name>htmlServlet</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <load-on-startup>3</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>htmlServlet</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>

これで、前の例と同様に、コントローラーで html ファイルと jsp ファイルの両方にアクセスできます。利点は、ソリューション 1 で必要だった "/someurl/" 余分なマッピングがないことです。コントローラーは次のようになります。

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home.html"); 

}

あなたのjspを指すには、まったく同じことをしています:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home_dynamic.jsp");

}

ソリューション 3 の実装:

3 番目の解決策は、解決策 1 と解決策 2 を組み合わせたものです。したがって、ここでは、*.html へのすべての要求を他のサーブレットに渡したいと考えています。独自のサーブレットを作成するか、既存のサーブレットの適切な候補を探すことができます。

上記のように、最初にビュー リゾルバーのプレフィックスとサフィックスをクリーンアップします。

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
  @Autowired
  @Qualifier("jstlViewResolver")
  private ViewResolver jstlViewResolver;

  @Bean
  @DependsOn({ "jstlViewResolver" })
  public ViewResolver viewResolver() {
    return jstlViewResolver;
  }

  @Bean(name = "jstlViewResolver")
  public ViewResolver jstlViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver(); // NOTE: this time I'm using InternalResourceViewResolver and again it does not matter :)
    resolver.setPrefix("");
    resolver.setSuffix("");
    return resolver;
  }

}

Tomcat の JspServlet を使用する代わりに、独自のサーブレットを作成します (または既存のサーブレットを再利用します)。

public class StaticFilesServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setCharacterEncoding("UTF-8");

    String resourcePath = request.getRequestURI();
    if (resourcePath != null) {
      FileReader reader = null;
      try {
        URL fileResourceUrl = request.getServletContext().getResource(resourcePath);
        String filePath = fileResourceUrl.getPath();

        if (!new File(filePath).exists()) {
          throw new IllegalArgumentException("Resource can not be found: " + filePath);
        }
        reader = new FileReader(filePath);

        int c = 0;
        while (c != -1) {
          c = reader.read();
          if (c != -1) {
            response.getWriter().write(c);
          }
        }

      } finally {
        if (reader != null) {
          reader.close();
        }
      }
    }
  }
}

*.html へのすべてのリクエストをサーブレットに渡すようにスプリングに指示します。

public class AppInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    WebApplicationContext context = new MyWebApplicationContext();
    servletContext.addListener(new ContextLoaderListener(context));

    addStaticHtmlFilesHandlingServlet(servletContext);
    addSpringDispatcherServlet(servletContext, context);

  }

 // (...)

  private void addStaticHtmlFilesHandlingServlet(ServletContext servletContext) {
    ServletRegistration.Dynamic servlet = servletContext.addServlet("HtmlsServlet", new StaticFilesServlet());
    servlet.setLoadOnStartup(1);
    servlet.addMapping("*.html");

  }

}

利点 (または欠点は、必要に応じて異なります) は、jsp タグが明らかに処理されないことです。あなたのコントローラはいつものように見えます:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home.html");

}

そしてjspの場合:

@Controller
public class HomeController {

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView home(Locale locale, Model model) {
        // (...)

        return new ModelAndView("/resources/home_dynamic.jsp");

}
于 2015-09-17T10:53:51.657 に答える
5

Resolver クラスはビュー クラスのリソースを解決するために使用され、ビュー クラスはリソースからビューを生成します。たとえば、以下のような典型的な InternalResourceViewResolver を使用します。

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
</beans:bean>

ビュー名 "home" は "/WEB-INT/views/home.jsp" としてマップされ、ビュー クラス InternalResourceView (JSP 用) を使用して JSP ビューに変換されます。サフィックス値を「.html」に置き換えると、Spring は特定のリソース「/WEB-INT/views/home.html」を取得できますが、それを生成する方法がわかりません。

于 2013-06-13T04:40:26.463 に答える
4

プレーンな .html ファイルは静的であり、特別な ViewResolver は必要ありません。ここに示すように、html ページ用の静的フォルダーを設定する必要があります。

例えば:

<mvc:resources mapping="/static/**" location="/static/" />
于 2013-12-03T10:25:19.257 に答える
0

マッピング *.html 用に登録されたサーブレットがない可能性があるため、この問題が発生します。
したがって、呼び出しは「デフォルトのサーブレット」で終了します。これは、おそらくあなたの DispatcherServlet である / のサーブレットマッピングに登録されています。
Dispatcher サーブレットは、home.html の要求を処理するコントローラーを見つけられないため、表示されるメッセージが表示されます。
この問題を解決するには、JSPServlet で処理される *.html 拡張子を登録すると、問題なく動作するはずです。

于 2014-02-18T05:35:25.443 に答える
0

InternalResourceViewResolverはサーブレットと jsp ファイルをサポートしていると思います。Spring の API javadocs によるサフィックスは、「URL の構築時にビュー名に追加される」ものです。非常に紛らわしいですが、これはファイルの拡張子ではありません。UrlBasedViewResolver setSuffix()クラスを確認しました。

おそらく、viewSuffix という名前を付けると、より意味のあるものになると思います。

于 2013-05-17T03:18:07.047 に答える