フォームの送信時にエラーが発生しました: ERROR 400: クライアントから送信された要求が構文的に正しくありません
サーブレット3.0を搭載したTomcat 7でHibernate 4、Spring 3、およびJSPページを使用しています
BaseEntity を拡張する Order クラスを取得しました ( BaseEntity は、自動生成される uuid メンバーを取得しました):
@Entity
@Table (name = "orders")
public class Order extends BaseEntity {
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "orders_additional_options", joinColumns = {
@JoinColumn(name = "orders_uuid", nullable = false, updatable = false) },
inverseJoinColumns = { @JoinColumn(name = "additionalOptions_uuid",
nullable = false, updatable = false) })
protected Set<AdditionalOption> selectedAdditionalOptions = new HashSet<AdditionalOption>();
/**
* Constructor
*/
public Order() {
super("");
}
/**
* @return the selectedAdditionalOptions
*/
public Set<AdditionalOption> getSelectedAdditionalOptions() {
return selectedAdditionalOptions;
}
/**
* @param selectedAdditionalOptions the selectedAdditionalOptions to set
*/
public void setSelectedAdditionalOptions(
Set<AdditionalOption> selectedAdditionalOptions) {
this.selectedAdditionalOptions = selectedAdditionalOptions;
}
}
順番に、AdditionalOption のリストを取得しました。AdditionalOption クラスは次のようになります。
@Entity
@Table (name = "additional_options")
public class AdditionalOption extends BaseEntity {
@Column (nullable = false)
protected String name;
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "selectedAdditionalOptions")
private Set<Order> orders = new HashSet<Order>();
/**
* Constructor
*/
public AdditionalOption()
{
super("");
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
ユーザーが追加オプションのリストからチェックボックスを選択できるようにするフォームで、新しい注文のフォームを含む JSP ページを作成します。
したがって、コードは次のようになります: ( uuid は BaseEntity から取得され、 availableAdditionalOptions は、AdditionalOptions テーブルの全選択クエリから取得されます)
<form:form modelAttribute="order" action="/menuapp/order/create" method="POST">
<table>
<tr>
<td>Additional Options:</td>
<td><form:checkboxes items="${availableAdditionalOptions}" path="selectedAdditionalOptions" itemLabel="name" itemValue="uuid"/></td>
</tr>
<tr>
<input value="Order" type="submit">
</tr>
</table>
</form:form>
そのため、ページは適切に表示されますが、送信ボタンをクリックするとエラーが表示されます: ERROR 400: The request sent by the client was syntaxally illegal
送信を処理するコントローラーは次のようになります。
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String save(Model model, @ModelAttribute Order order) {
orderService.saveOrUpdate(order);
model.addAttribute("saved", "success");
return "order";
}
しかし一向に届かない…
HTML POSTリクエストで送信されるものをwiresharkで確認したところ、次のようになりました。
selectedAdditionalOptions=ae396f42-843c-454d-a573-85e71c36709d&selectedAdditionalOptions=962e0766-5e56-4490-bc50-d4f41272c77e&_selectedAdditionalOptions=on
log4j ファイルに次のエラーが見つかりました。
013-11-04 20:46:56 DEBUG DispatcherServlet:823 - DispatcherServlet with name 'appServlet' processing POST request for [/menuapp/order/create]
2013-11-04 20:46:56 DEBUG RequestMappingHandlerMapping:220 - Looking up handler method for path /order/create
2013-11-04 20:46:56 DEBUG RequestMappingHandlerMapping:227 - Returning handler method [public java.lang.String com.openu.menuapp.controller.OrderController.save(org.springframework.ui.Model,com.openu.menuapp.entity.Order)]
2013-11-04 20:46:56 DEBUG DefaultListableBeanFactory:246 - Returning cached instance of singleton bean 'orderController'
2013-11-04 20:46:56 DEBUG BeanUtils:443 - No property editor [com.openu.menuapp.entity.AdditionalOptionEditor] found for type com.openu.menuapp.entity.AdditionalOption according to 'Editor' suffix convention
2013-11-04 20:46:56 DEBUG ExceptionHandlerExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.openu.menuapp.controller.OrderController.save(org.springframework.ui.Model,com.openu.menuapp.entity.Order)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'order' on field 'selectedAdditionalOptions': rejected value [962e0766-5e56-4490-bc50-d4f41272c77e]; codes [typeMismatch.order.selectedAdditionalOptions,typeMismatch.selectedAdditionalOptions,typeMismatch.java.util.Set,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.selectedAdditionalOptions,selectedAdditionalOptions]; arguments []; default message [selectedAdditionalOptions]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Set' for property 'selectedAdditionalOptions'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.openu.menuapp.entity.AdditionalOption] for property 'selectedAdditionalOptions[0]': no matching editors or conversion strategy found]
2013-11-04 20:46:56 DEBUG ResponseStatusExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.openu.menuapp.controller.OrderController.save(org.springframework.ui.Model,com.openu.menuapp.entity.Order)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'order' on field 'selectedAdditionalOptions': rejected value [962e0766-5e56-4490-bc50-d4f41272c77e]; codes [typeMismatch.order.selectedAdditionalOptions,typeMismatch.selectedAdditionalOptions,typeMismatch.java.util.Set,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.selectedAdditionalOptions,selectedAdditionalOptions]; arguments []; default message [selectedAdditionalOptions]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Set' for property 'selectedAdditionalOptions'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.openu.menuapp.entity.AdditionalOption] for property 'selectedAdditionalOptions[0]': no matching editors or conversion strategy found]
2013-11-04 20:46:56 DEBUG DefaultHandlerExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.openu.menuapp.controller.OrderController.save(org.springframework.ui.Model,com.openu.menuapp.entity.Order)]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'order' on field 'selectedAdditionalOptions': rejected value [962e0766-5e56-4490-bc50-d4f41272c77e]; codes [typeMismatch.order.selectedAdditionalOptions,typeMismatch.selectedAdditionalOptions,typeMismatch.java.util.Set,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [order.selectedAdditionalOptions,selectedAdditionalOptions]; arguments []; default message [selectedAdditionalOptions]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Set' for property 'selectedAdditionalOptions'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.openu.menuapp.entity.AdditionalOption] for property 'selectedAdditionalOptions[0]': no matching editors or conversion strategy found]
2013-11-04 20:46:56 DEBUG DispatcherServlet:999 - Null ModelAndView returned to DispatcherServlet with name 'appServlet': assuming HandlerAdapter completed request handling
2013-11-04 20:46:56 DEBUG DispatcherServlet:966 - Successfully completed request
私は自分の質問に答えることができないので、ここに答えを書きます:
OK、問題はUuid as StringからAdditionalOptionオブジェクトへのコンバーターでした
そのため、次のバインディングをコントローラーに追加します。
@Autowired
private AdditionalOptionConvertor additionalOptionConvertor;
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(AdditionalOption.class, additionalOptionConvertor);
}
AdditionalOptionConvertor が Autowired を取得する Service であり、次のようになっている場合:
@Service("additionalOptionConvertor")
public class AdditionalOptionConvertor extends BaseConvertor<AdditionalOption>
{
@Autowired
protected AdditionalOptionService service;
@Override
public void setAsText(String text) throws IllegalArgumentException
{
baseService = service;
super.setAsText(text);
}
}
すべてのオブジェクトが Uuid メンバーを共有し、すべてのサービスが BaseEntity オブジェクトを返す findByUuid を取得するため、BaseConvertor クラスも追加します。したがって、BaseConvertor は次のようになります。
public abstract class BaseConvertor<T extends BaseEntity> extends PropertyEditorSupport
{
protected BaseEntityService baseService;
@Override
public void setAsText(String text) throws IllegalArgumentException
{
T value = baseService.findByUUID(text);
setValue(value);
}
@SuppressWarnings("unchecked")
@Override
public String getAsText()
{
T d = (T) getValue();
return d != null ? String.valueOf(d.getUuid()) : "";
}
}
PropertyEditorSupport の詳細については、 検証、データ バインディング、型変換を参照してください。
ヘルパーに感謝