2015-05-12 4 views
6

У меня возникла сложная проблема с приложением Spring Boot, которое я пытался решить некоторое время, и я надеюсь, что кто-то может мне помочь. Я удалил все остальные части проекта и постарался сделать его максимально простым. Если вы перейдете на localhost: 8080, появится форма с двумя текстовыми полями для ввода двух имен и кнопки «Отправить». Первое имя будет сохранено в объекте Nominee, второе - в объекте Submitter. Когда вы нажмете «Отправить», он выполнит проверку в полях, чтобы убедиться, что ни один из них не пуст. Я отправлю код ниже и объясню свою проблему в конце.Проверка весны формы с помощью двух объектов

Application.java

package nomination; 
 

 
import org.springframework.boot.SpringApplication; 
 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
 
import org.springframework.context.annotation.Bean; 
 
import org.springframework.jms.annotation.EnableJms; 
 
import org.springframework.jms.config.JmsListenerContainerFactory; 
 
import org.springframework.jms.config.SimpleJmsListenerContainerFactory; 
 
import org.springframework.web.servlet.config.annotation.EnableWebMvc; 
 

 
@SpringBootApplication 
 
@EnableJms 
 
@EnableWebMvc 
 
public class Application { 
 

 
    public static void main(String[] args) throws Exception { 
 
     // Launch the application 
 
     SpringApplication.run(Application.class, args); 
 
    } 
 

 
}

WebController.java

package nomination; 
 

 
import javax.validation.Valid; 
 
import org.slf4j.Logger; 
 
import org.slf4j.LoggerFactory; 
 
import org.springframework.ui.Model; 
 
import org.springframework.web.bind.WebDataBinder; 
 
import org.springframework.web.bind.annotation.InitBinder; 
 
import org.springframework.web.bind.annotation.ModelAttribute; 
 
import org.springframework.stereotype.Controller; 
 
import org.springframework.validation.BindingResult; 
 
import org.springframework.web.bind.annotation.RequestMapping; 
 
import org.springframework.web.bind.annotation.RequestMethod; 
 
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 
 
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 
 

 
@Controller 
 
public class WebController extends WebMvcConfigurerAdapter { 
 
    protected static final Logger LOG = LoggerFactory.getLogger(WebController.class); 
 

 
    @InitBinder("nominee") 
 
    protected void initNomineeBinder(WebDataBinder binder) { 
 
     binder.setValidator(new NomineeValidator()); 
 
    } 
 

 
    @InitBinder("submitter") 
 
    protected void initSubmitterBinder(WebDataBinder binder) { 
 
     binder.setValidator(new SubmitterValidator()); 
 
    } 
 

 
    @Override 
 
    public void addViewControllers(ViewControllerRegistry registry) { 
 
     registry.addViewController("/success").setViewName("success"); 
 
    } 
 

 
    @RequestMapping(value="/", method=RequestMethod.GET) 
 
    public String showForm(Model model) { 
 
     model.addAttribute("nominee", new Nominee()); 
 
     model.addAttribute("submitter", new Submitter()); 
 
     return "form"; 
 
    } 
 

 
    @RequestMapping(value="/", method=RequestMethod.POST) 
 
    public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee, 
 
            @ModelAttribute(value="submitter") @Valid Submitter submitter, 
 
            BindingResult bindingResult, @Valid Model model) { 
 
     LOG.info("Nominee to string: " + nominee.toString()); 
 
     LOG.info("Submitter to string: " + submitter.toString()); 
 
     LOG.info("bindingResult to string: " + bindingResult.toString()); 
 
     if (bindingResult.hasErrors()) { 
 
      return "form"; 
 
     } 
 

 
     return "redirect:/success"; 
 
    } 
 

 
}

Nominee.java

package nomination; 
 

 
import lombok.Data; 
 

 
@Data 
 
public class Nominee { 
 
    private String name; 
 
}

NomineeValidatior.java

package nomination; 
 

 
import org.springframework.validation.Errors; 
 
import org.springframework.validation.ValidationUtils; 
 
import org.springframework.validation.Validator; 
 

 
public class NomineeValidator implements Validator { 
 

 
    public boolean supports(Class clazz) { 
 
     return Nominee.class.equals(clazz); 
 
    } 
 

 
    public void validate(Object object, Errors errors) { 
 
     ValidationUtils.rejectIfEmpty(errors, "name", "name", "This field is empty."); 
 
    } 
 
}

Submitter.java

package nomination; 
 

 
import lombok.Data; 
 

 
@Data 
 
public class Submitter { 
 
    private String sname; 
 
}

SubmitterValidator.java

package nomination; 
 

 
import org.springframework.validation.Errors; 
 
import org.springframework.validation.ValidationUtils; 
 
import org.springframework.validation.Validator; 
 

 
public class SubmitterValidator implements Validator { 
 

 
    public boolean supports(Class clazz) { 
 
     return Submitter.class.equals(clazz); 
 
    } 
 

 
    public void validate(Object object, Errors errors) { 
 
     ValidationUtils.rejectIfEmpty(errors, "sname", "sname", "This field is empty."); 
 
    } 
 
}

form.html

<html><body> 
 
    <form role="form" th:action="@{/}" method="post"> 
 
     <h2>Nominee details</h2> 
 
     <table> 
 
      <tr> 
 
       <td>Name:</td> 
 
       <td> 
 
        <input type="text" th:field="${nominee.name}" /> 
 
       </td> 
 
       <td><p th:if="${#fields.hasErrors('nominee.name')}" th:errors="${nominee.name}">Empty field</p></td> 
 
      </tr> 
 
     </table> 
 
     <h2>Your details</h2> 
 
     <table> 
 
      <tr> 
 
       <td>Your name:</td> 
 
       <td> 
 
        <input type="text" th:field="${submitter.sname}" /> 
 
       </td> 
 
       <td><p th:if="${#fields.hasErrors('submitter.sname')}" th:errors="${submitter.sname}">Empty field</p></td> 
 
      </tr> 
 
     </table> 
 
     <div> 
 
      <button type="submit">Submit nomination</button> 
 
     </div> 
 
    </form> 
 
</body></html>

успех.

: HTML

<html><body>Form successfully submitted.</body></html>

Если я оставлю первое текстовое поле пустым (и заполнить или не заполнять во втором текстовом поле), на экране, который читает появляется сообщение об ошибке

Ошибка Whitelabel Page

Это приложение не имеет явного отображения для/ошибки, так что вы видите это как резерв. Вт 12 мая 13:10:17 AEST 2015 Была непредвиденная ошибка (type = Bad Request, status = 400). Не удалось выполнить проверку для object = 'nominee'. Количество ошибок: 1

Я не знаю, как исправить это, так что оставление первого текстового поля пустым не вызовет страницу с ошибкой whitelabel. Если я оставляю второе текстовое поле пустым, но заполняю первое, оно действует совершенно нормально, поэтому я не уверен, почему он вызывает ошибку, если я попробую по-другому. Любая помощь, связанная с этим, будет очень признательна.

Кроме того, возможно, вы заметили, что мне приходилось использовать «имя» и «змей» в качестве моих переменных в Номинанте и Отправитель, если я установил их как «имя», то он не работает должным образом. Если есть способ отредактировать его, чтобы они оба могли использовать «имя», мне было бы интересно узнать, как это сделать.

Редактировать: Решение проблемы. В WebController CheckPersonInfo нуждается в отдельном BindingResult для каждого проверяемого объекта. BindingResult должен быть в параметрах метода сразу после каждого объекта @Valid.

Так, в WebController.java, это:

@RequestMapping(value="/", method=RequestMethod.POST) 
public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee, 
           @ModelAttribute(value="submitter") @Valid Submitter submitter, 
           BindingResult bindingResult, @Valid Model model) { 
    LOG.info("Nominee to string: " + nominee.toString()); 
    LOG.info("Submitter to string: " + submitter.toString()); 
    LOG.info("bindingResult to string: " + bindingResult.toString()); 
    if (bindingResult.hasErrors()) { 
     return "form"; 
    } 

    return "redirect:/success"; 
} 

должно стать следующее:

@RequestMapping(value="/", method=RequestMethod.POST) 
public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee, 
           BindingResult bindingResultNominee, 
           @ModelAttribute(value="submitter") @Valid Submitter submitter, 
           BindingResult bindingResultSubmitter) { 
    LOG.info("Nominee to string: " + nominee.toString()); 
    LOG.info("Submitter to string: " + submitter.toString()); 
    if (bindingResultNominee.hasErrors() || bindingResultSubmitter.hasErrors()) { 
     return "form"; 
    } 

    return "redirect:/success"; 
} 

(объектная модель была удалена, так как он никогда на самом деле использовать в любом месте, если вам нужно проверить это с помощью @Valid, тогда вы добавите третий объект BindingResult.)

+0

Попробуйте запустить logging для 'org.springframework.web' в DEBUG, чтобы узнать, что консоль говорит что-то интересное. – chrylis

+0

Просто интересно узнать. Почему вы собираетесь на проверку на стороне сервера вместо проверки на стороне клиента (на основе javascript) здесь – Thomas

+0

Нет настоящей причины. Используя Spring Boot в первый раз, просто играя с ним, чтобы научить себя, как это работает, увидел, что у него была своя реализация проверки формы и он хотел попытаться заставить его работать. – Jordan

ответ

7

Найдено решение. В WebController checkPersonInfo нуждается в отдельном BindingResult для каждого проверяемого объекта. BindingResult должен быть в параметрах метода сразу после каждого объекта @Valid.

Итак, в WebController.Java, это:

@RequestMapping(value="/", method=RequestMethod.POST) 
public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee, 
           @ModelAttribute(value="submitter") @Valid Submitter submitter, 
           BindingResult bindingResult, @Valid Model model) { 
    LOG.info("Nominee to string: " + nominee.toString()); 
    LOG.info("Submitter to string: " + submitter.toString()); 
    LOG.info("bindingResult to string: " + bindingResult.toString()); 
    if (bindingResult.hasErrors()) { 
     return "form"; 
    } 

    return "redirect:/success"; 
} 

должно стать следующее:

@RequestMapping(value="/", method=RequestMethod.POST) 
public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee, 
           BindingResult bindingResultNominee, 
           @ModelAttribute(value="submitter") @Valid Submitter submitter, 
           BindingResult bindingResultSubmitter) { 
    LOG.info("Nominee to string: " + nominee.toString()); 
    LOG.info("Submitter to string: " + submitter.toString()); 
    if (bindingResultNominee.hasErrors() || bindingResultSubmitter.hasErrors()) { 
     return "form"; 
    } 

    return "redirect:/success"; 
} 

(объектная модель была удалена, так как он никогда на самом деле использовать в любом месте, если вам нужно, чтобы проверить его с @Valid, то вы бы добавить третий объект BindingResult.)

2

Обычный случай - использовать объекты Dto для этого случая. Это означает, что вы создаете объект, содержащий все соответствующие поля формы, и проверяете на основе этого.

Например:

@Data 
public class MyDto { 

    private Nominee nominee; 

    private Submitter submitter; 
} 

@RequestMapping(value="/", method=RequestMethod.POST) 
public String checkPersonInfo(@Valid MyDto dto, BindingResult bindingResult) { 
    LOG.info("Nominee to string: " + dto.getNominee().toString()); 
    LOG.info("Submitter to string: " + dto.getSubmitter().toString()); 
    LOG.info("bindingResult to string: " + bindingResult.toString()); 
    if (bindingResult.hasErrors()) { 
     return "form"; 
    } 
    return "redirect:/success"; 
} 

Кроме того, вы можете добавить что-то подобное к контроллеру.

@ExceptionHandler(Throwable.class) 
public ModelAndView processError(Throwable ex) { 
    ex.toString(); 
    // do something and return a ModelAndView object as you like. 
} 
Смежные вопросы