これは、これらのタイプの質問に対する広範な標準的な質問と回答の投稿になることを意図しています。
ユーザーがインメモリコレクションに映画名を追加できるSpring MVC Webアプリケーションを作成しようとしています。そのように構成されています
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[] { "/" };
}
}
そして
@Configuration
@ComponentScan("com.example")
public class SpringServletConfig extends WebMvcConfigurationSupport {
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
}
@Controller
パッケージには単一のcom.example
クラスがあります
@Controller
public class MovieController {
private final CopyOnWriteArrayList<Movie> movies = new CopyOnWriteArrayList<>();
@RequestMapping(path = "/movies", method = RequestMethod.GET)
public String homePage(Model model) {
model.addAttribute("movies", movies);
return "index";
}
@RequestMapping(path = "/movies", method = RequestMethod.POST)
public String upload(@ModelAttribute("movie") Movie movie, BindingResult errors) {
if (!errors.hasErrors()) {
movies.add(movie);
}
return "redirect:/movies";
}
public static class Movie {
private String filmName;
public String getFilmName() {
return filmName;
}
public void setFilmName(String filmName) {
this.filmName = filmName;
}
}
}
WEB-INF/jsps/index.jsp
を含む
<%@ taglib prefix="c" uri="http://Java.Sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Movies</title>
</head>
<body>
Current Movies:
<c:forEach items="${movies}" var="movieItem">
<ul>
<li>${movieItem.filmName}</li>
</ul>
</c:forEach>
<form:form>
<div>Movie name:</div>
<form:input path="filmName" type="text" id="name" />
<input type="submit" value="Upload">
</form:form>
</body>
</html>
アプリケーションは、コンテキストパス/Example
で構成されます。 GETリクエストを送信するとき
http://localhost:8080/Example/movies
リクエストは失敗し、Spring MVCは500ステータスコードで応答し、次の例外とスタックトレースを報告します
Java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.Java:144)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.Java:168)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.Java:188)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.Java:154)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.Java:117)
org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.Java:422)
org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.Java:142)
org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.Java:84)
org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.Java:80)
org.Apache.jsp.WEB_002dINF.jsps.index_jsp._jspx_meth_form_005finput_005f0(index_jsp.Java:267)
org.Apache.jsp.WEB_002dINF.jsps.index_jsp._jspx_meth_form_005fform_005f0(index_jsp.Java:227)
org.Apache.jsp.WEB_002dINF.jsps.index_jsp._jspService(index_jsp.Java:142)
org.Apache.jasper.runtime.HttpJspBase.service(HttpJspBase.Java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.Java:729)
org.Apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.Java:438)
org.Apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.Java:396)
org.Apache.jasper.servlet.JspServlet.service(JspServlet.Java:340)
javax.servlet.http.HttpServlet.service(HttpServlet.Java:729)
org.Apache.Tomcat.websocket.server.WsFilter.doFilter(WsFilter.Java:52)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.Java:168)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.Java:303)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.Java:1257)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.Java:1037)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.Java:980)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.Java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.Java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.Java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.Java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.Java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.Java:729)
org.Apache.Tomcat.websocket.server.WsFilter.doFilter(WsFilter.Java:52)
JSPは、POSTリクエストを送信するために使用できるMovie
名と送信ボタンの単一のテキスト入力でHTML <form>
を生成することを期待していました新しいMovie
。代わりに、JSPサーブレットがSpringの<form:form>
タグのレンダリングに失敗するのはなぜですか?
Spring MVCのform tag を使用しようとしています。
このタグは、HTML
form
タグをレンダリングし、バインディング用の内部タグへのバインディングパスを公開します。コマンドオブジェクトをPageContext
に配置して、内部タグからコマンドオブジェクトにアクセスできるようにします。 [..]
User
というドメインオブジェクトがあるとします。firstName
やlastName
などのプロパティを持つJavaBeanです。 _form.jsp
_を返すフォームコントローラーのformバッキングオブジェクトとして使用します。
つまり、Spring MVCはコマンドオブジェクトを抽出し、 path
または などのform
の内部タグのinput
式をバインドするための青写真としてそのタイプを使用しますcheckbox
、HTMLのform
要素をレンダリングします。
このコマンドオブジェクトはモデル属性とも呼ばれ、その名前はform
タグのmodelAttribute
またはcommandName
属性で指定されます。 JSPで省略しました
_<form:form>
_
名前を明示的に指定することもできます。これらは両方とも同等です。
_<form:form modelAttribute="some-example-name">
<form:form commandName="some-example-name">
_
default属性名はcommand
(エラーメッセージに表示されるもの)です。モデル属性は、アプリケーションがSpring MVCスタックに提供するオブジェクト、通常はPOJOまたはPOJOのコレクションであり、Spring MVCスタックがビューに公開します(MVのMからVへ)。
Spring MVCは、すべてのモデル属性を ModelMap
(それらはすべて名前を持っています)に収集し、JSPの場合、それらを HttpServletRequest
属性に転送します。タグとEL式はそれらにアクセスできます。
この例では、パス_@Controller
_へのGET
を処理する_/movies
_ハンドラーメソッドが単一のモデル属性を追加します
_model.addAttribute("movies", movies); // not named 'command'
_
その後、_index.jsp
_に転送します。次に、このJSPはレンダリングしようとします
_<form:form>
...
<form:input path="name" type="text" id="name" />
...
</form:form>
_
これをレンダリングする際、 FormTag
(実際には、 InputTag
)はcommand
(デフォルトの属性名)という名前のモデル属性を見つけようとし、 name
式と対応するプロパティ値から構築されたpath
属性を持つHTML _<input>
_要素。 Movie#getFilmName()
の結果。
見つからないため、表示される例外をスローします
_Java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
_
JSPエンジンはそれをキャッチし、500ステータスコードで応答します。 Movie
POJOを利用してフォームを正しく構築したい場合は、モデル属性を明示的に追加できます。
_model.addAttribute("movie", new Movie());
_
またはSpring MVCに作成して追加してもらいます(アクセス可能なパラメータなしのコンストラクタが必要です)
_@RequestMapping(path = "/movies", method = RequestMethod.GET)
public String homePage(@ModelAttribute("command") Movie movie, Model model) {...}
_
または、_@ModelAttribute
_クラスに_@Controller
_アノテーション付きメソッドを含める
_@ModelAttribute("command")
public Movie defaultInstance() {
Movie movie = new Movie();
movie.setFilmName("Rocky II");
return movie;
}
_
Spring MVCはこのメソッドを呼び出し、返されたオブジェクトを、囲んでいる_@Controller
_によって処理される各リクエストのモデル属性に暗黙的に追加することに注意してください。
この説明から、Springのform
タグは、実際の値を使用して、既存のオブジェクトからHTML _<form>
_をレンダリングするのにより適していると推測できます。単純に空の_<form>
_を作成する場合は、モデル属性に依存せずに自分で作成する方が適切な場合があります。
_<form method="post" action="${pageContext.request.contextPath}/movies">
<input name="filmName" type="text" />
<input type="submit" value="Upload" />
</form>
_
受信側のPOST
ハンドラーメソッドは、filmName
入力値を抽出し、それを使用してMovie
オブジェクトを初期化できます。
これまで見てきたように、FormTag
はデフォルトで、またはcommand
またはmodelAttribute
で指定された名前を持つcommandName
という名前のモデル属性を探します。正しい名前を使用していることを確認してください。
ModelMap
には、追加する addAttribute(Object)
メソッドがあります
generated の名前を使用して、この
Map
に提供された属性。
一般的な慣習は
javaBeansプロパティの命名規則に従って、[属性]
Class
の大文字ではない短縮名を返します。したがって、_com.myapp.Product
_はproduct
になります。 _com.myapp.MyProduct
_はmyProduct
になります。 _com.myapp.UKProduct
_はUKProduct
になります
この(または同様の)メソッドを使用している場合、またはモデル属性を表す_@RequestMapping
_ supported return types のいずれかを使用している場合は、生成される名前があなたが期待するもの。
別の一般的なエラーは、_@Controller
_メソッドを完全にバイパスすることです。典型的なSpring MVCアプリケーションは、次のパターンに従います。
DispatcherServlet
は、リクエストを処理する_@RequestMapping
_メソッドを選択しますDispatcherServlet
はモデル属性をHttpServletRequest
に追加し、ビュー名に対応するJSPにリクエストを転送します何らかの設定ミスにより_@RequestMapping
_メソッドを完全にスキップした場合、属性は追加されません。これは起こりうる
WEB-INF
_外、またはwelcome-list
_の_web.xml
_にJSPリソースが含まれている場合、サーブレットコンテナは、Spring MVCスタックを完全にバイパスして、それを直接レンダリングします何らかの方法で、モデル属性が適切に追加されるように_@Controller
_を呼び出す必要があります。
BindingResult
はこれと何の関係がありますか?BindingResult
は、モデル属性の初期化または検証のためのコンテナです。 Spring MVC documentation 状態
Errors
またはBindingResult
パラメーターは、メソッドシグネチャに複数のモデルオブジェクトが含まれる場合があり、Springはそれぞれに個別のBindingResult
インスタンスを作成するため、すぐにバインドされるモデルオブジェクトに従う必要があります[...]
つまり、BindingResult
を使用する場合は、_@RequestMapping
_メソッドの対応するモデル属性パラメーターに従う必要があります
_@RequestMapping(path = "/movies", method = RequestMethod.POST)
public String upload(@ModelAttribute("movie") Movie movie, BindingResult errors) {
_
BindingResult
オブジェクトもモデル属性と見なされます。 Spring MVCはシンプルな命名規則を使用してそれらを管理し、対応する通常のモデル属性を簡単に見つけられるようにします。 BindingResult
にはモデル属性に関するより多くのデータ(検証エラーなど)が含まれているため、FormTag
は最初にモデル属性にバインドしようとします。しかし、それらは手をつないで行くので、一方が他方なしで存在する可能性は低いです。
Formタグで物事を単純にするために、実際に探しているものの恐ろしい名前である「commandName」を追加するだけです。MdelAttributeアノテーションで指定したオブジェクトが必要です。したがって、この場合はcommandName = "movie"です。
長い説明の友人を読むのを節約できます。
私の場合、modelAttribute="movie"
をformタグに追加し、<form:input path="filmName" type="text" id="movie.name" />
のようなモデル名を属性に追加することで機能しました
検索を行う複数のフォームがある画面でこのエラーが発生しました。各フォームは、同じ画面に結果が表示される独自のコントローラーメソッドに投稿します。
問題:他の2つのフォームを各コントローラーメソッドのモデル属性として追加できなかったため、画面に結果が表示されるときにエラーが発生しました。
Form1 -> bound to Bean1 (bean1) -> Posting to /action1
Form2 -> bound to Bean2 (bean2) -> Posting to /action2
Form3 -> bound to Bean3 (bean2) -> Posting to /action3
@PostMapping
public String blah(@ModelAttribute("bean1") Bean1 bean, Model model){
// do something with bean object
// do not miss adding other 2 beans as model attributes like below.
model.addAttribute("bean2", new Bean2());
model.addAttribute("bean3", new Bean3());
return "screen";
}
@PostMapping
public String blahBlah(@ModelAttribute("bean2") Bean2 bean, Model model){
// do something with bean object
// do not miss adding other 2 beans as model attributes like below.
model.addAttribute("bean1", new Bean1());
model.addAttribute("bean3", new Bean3());
return "screen";
}
@PostMapping
public String blahBlahBlah(@ModelAttribute("bean3") Bean3 bean, Model model){
// do something with bean object
// do not miss adding other 2 beans as model attributes like below.
model.addAttribute("bean1", new Bean1());
model.addAttribute("bean2", new Bean2());
return "screen";
}