Spring 如果多部分文件参数为null,则ModelAttribute的多部分/表单数据绑定将失败

Spring 如果多部分文件参数为null,则ModelAttribute的多部分/表单数据绑定将失败,spring,spring-mvc,spring-boot,recaptcha,thymeleaf,Spring,Spring Mvc,Spring Boot,Recaptcha,Thymeleaf,我有一个控制器映射处理上传的文件 控制器 @RequestMapping(value = "/careers/pursue", method = RequestMethod.POST) public Callable<String> pursue( final @RequestParam("g-recaptcha-response") String captchaResponse, final @RequestParam("file") Multipa

我有一个控制器映射处理上传的文件

控制器

  @RequestMapping(value = "/careers/pursue", method = RequestMethod.POST)
  public Callable<String> pursue(
      final @RequestParam("g-recaptcha-response") String captchaResponse,
      final @RequestParam("file") MultipartFile file,
      final @ModelAttribute("jobapplication") @Valid JobApplication application, final BindingResult bindingResult,
      final Model model)  
@RequestMapping(value = "/upload", method = RequestMethod.POST)
    public String pursue(
            final @RequestParam("g-recaptcha-response") String captchaResponse,
            final @RequestParam("file") MultipartFile file,
            final @ModelAttribute("jobapplication") @Valid JobApplication application, final BindingResult bindingResult,
            final Model model)
    {
        if (bindingResult.hasErrors() || captchaResponse.length() == 0 || file.isEmpty())
        {
            return "form";
        }

        return "redirect:/";
    }
或者在未验证验证码的情况下尝试提交,我收到此异常

Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (jobs:91)
....
....
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'jobapplication' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:144)
at org.thymeleaf.spring4.util.FieldUtils.getBindStatusFromParsedExpression(FieldUtils.java:396)
at org.thymeleaf.spring4.util.FieldUtils.getBindStatus(FieldUtils.java:323)
at org.thymeleaf.spring4.util.FieldUtils.getBindStatus(FieldUtils.java:289)
at org.thymeleaf.spring4.processor.attr.AbstractSpringFieldAttrProcessor.processAttribute(AbstractSpringFieldAttrProcessor.java:98)
at org.thymeleaf.processor.attr.AbstractAttrProcessor.doProcess(AbstractAttrProcessor.java:87)
at org.thymeleaf.processor.AbstractProcessor.process(AbstractProcessor.java:212)
... 66 common frames omitted
这也无济于事。我是否必须进行扩展,或者需要进行更改/修复,或者我是否遗漏了一些小细节

更新

添加控制器方法

  @RequestMapping(value = "/careers/pursue", method = RequestMethod.POST)
  public Callable<String> pursue(final @ModelAttribute("jobapplication") @Valid JobApplication application,
      final BindingResult bindingResult, final Model model,
      final @RequestParam(value = "g-recaptcha-response", required = false) String captchaResponse,
      final @RequestPart(value = "file", required = false) MultipartFile file) {
    return new Callable<String>() {
      @Override
      public String call() throws Exception {
        try {
          model.asMap().clear();
          GoogleCaptchaResponseData response = captchaVerifier.isCaptchaResponseValid(captchaResponse).get();
          model.addAttribute("recaptcha", response.isSuccess());
          model.addAttribute("recaptchamessage", response.getErrorCodes());

          if (response.isSuccess() && !file.isEmpty()) {
            byte[] bytes = file.getBytes();

            LOGGER.info("Found file of type {}", file.getOriginalFilename());
            ByteArrayInputStream inputBytes = new ByteArrayInputStream(bytes);
            mailApi.sendMail(mailApi.buildJobApplicationEmail(application, new BufferedInputStream(inputBytes)));
            model.asMap().clear();
            model.addAttribute("uploadsuccess", true);
            model.addAttribute("resource_host", resourceHost);
            model.addAttribute("jobapplication", new JobApplication());
          }
        } catch (InterruptedException | ExecutionException e) {
          LOGGER.error(e.getMessage(), e);
          model.asMap().clear();
          model.addAttribute("jobapplication", application);
          model.addAttribute("resource_host", resourceHost);
          model.addAttribute("uploadsuccess", false);
        }
        return "jobs";
      }
    };
  }
@RequestMapping(value=“/careers/purse”,method=RequestMethod.POST)
公共可调用追踪(final@modeldattribute(“jobapplication”)@有效的jobapplication应用程序,
最终绑定结果绑定结果,最终模型,
final@RequestParam(value=“g-recaptcha-response”,required=false)字符串captchaResponse,
最终@RequestPart(value=“file”,required=false)多部分文件{
返回新的可调用(){
@凌驾
公共字符串调用()引发异常{
试一试{
model.asMap().clear();
GoogleCaptchaResponseData response=captchavifier.isCaptchaResponseValid(captchaResponse.get();
model.addAttribute(“recomptcha”,response.issucess());
addAttribute(“recaptchamessage”,response.getErrorCodes());
if(response.issucess()&&!file.isEmpty()){
byte[]bytes=file.getBytes();
info(“找到{}类型的文件”,file.getOriginalFilename());
ByteArrayInputStream inputBytes=新的ByteArrayInputStream(字节);
mailApi.sendMail(mailApi.buildJobApplicationMail(应用程序,新的BufferedInputStream(inputBytes));
model.asMap().clear();
model.addAttribute(“uploadsuccess”,true);
model.addAttribute(“资源\主机”,resourceHost);
addAttribute(“jobapplication”,new jobapplication());
}
}捕获(中断异常|执行异常e){
LOGGER.error(e.getMessage(),e);
model.asMap().clear();
model.addAttribute(“作业应用程序”,应用程序);
model.addAttribute(“资源\主机”,resourceHost);
model.addAttribute(“uploadsuccess”,false);
}
返回“工作”;
}
};
}
  • 您的控制器
    @RequestMapping
    未映射到与表单相同的路径
  • 在表单中,确保文件和验证码的输入名称与
    @RequestParam
  • @RequestParam
    没有传递我正在查找的
    length()==0
    file.isEmpty()
  • 此外,您可能还想看看

    控制器

      @RequestMapping(value = "/careers/pursue", method = RequestMethod.POST)
      public Callable<String> pursue(
          final @RequestParam("g-recaptcha-response") String captchaResponse,
          final @RequestParam("file") MultipartFile file,
          final @ModelAttribute("jobapplication") @Valid JobApplication application, final BindingResult bindingResult,
          final Model model)  
    
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
        public String pursue(
                final @RequestParam("g-recaptcha-response") String captchaResponse,
                final @RequestParam("file") MultipartFile file,
                final @ModelAttribute("jobapplication") @Valid JobApplication application, final BindingResult bindingResult,
                final Model model)
        {
            if (bindingResult.hasErrors() || captchaResponse.length() == 0 || file.isEmpty())
            {
                return "form";
            }
    
            return "redirect:/";
        }
    
    表格

    <form name="jobsForm" id="jobsForm" novalidate="novalidate" action="#" th:action="@{/careers/pursue}"
                        th:object="${jobapplication}" method="post" enctype="multipart/form-data">
        <div class="control-group form-group">
            <div class="controls">
                <label>First Name:</label>
                <input type="text" class="form-control" id="firstName" th:field="*{firstName}" required="required" data-validation-required-message="Please enter your name." />
                <p class="help-block"></p>
            </div>
        </div>
        <div class="control-group form-group">
            <div class="controls">
                <label>Last Name:</label>
                <input type="text" class="form-control" id="lastName" th:field="*{lastName}" required="required" data-validation-required-message="Please enter your name." />
                <p class="help-block"></p>
            </div>
        </div>                    
        <div class="control-group form-group">
            <div class="controls">
                <label>Phone Number:</label>
                <input type="tel" class="form-control" id="phone" th:field="*{phone}" required="required" data-validation-required-message="Please enter your phone number." />
            </div>
        </div>
        <div class="control-group form-group">
            <div class="controls">
                <label>Email Address:</label>
                <input type="email" class="form-control" id="email" th:field="*{email}" required="required" data-validation-required-message="Please enter your email address."/>
            </div>
        </div>
        <div class="control-group form-group">
            <div class="controls">
                <label>Role:</label>
                <input type="email" class="form-control" id="role" th:field="*{role}" required="required" data-validation-required-message="Please enter your email address."/>
            </div>
        </div>                    
        <div class=" control-group form-group">
                                <div class="g-recaptcha" data-sitekey="ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"></div>                    
        </div>
        <div class=" control-group form-group">
             <span class="btn btn-primary btn-file">
                                    Add your Resumé <input type="file" name="file" id="file" required="required"/>
                             </span>
        </div>
        <div id="success"></div>
        <!-- For success/fail messages -->
        <button type="submit" class="btn btn-primary">Apply!</button>
    </form>
    
    <form name="jobsForm" id="jobsForm" novalidate="novalidate" action="#" th:action="@{/upload}"
          th:object="${jobapplication}" method="post" enctype="multipart/form-data">
        <span>First Name: </span>
        <input id="firstname" th:field="*{firstName}" type="text"/><br/>
        <span>Last Name: </span>
        <input id="lastname" th:field="*{lastName}" type="text"/><br/>
        <span>Phone: </span>
        <input id="phone" th:field="*{phone}" type="text"/><br/>
        <span>Email: </span>
        <input id="email" th:field="*{email}" type="text"/><br/>
        <span>Role: </span>
        <input id="role" th:field="*{role}" type="text"/><br/>
        <span></span>
        <input type="text" name="g-recaptcha-response"/><br/>
        <span>File: </span>
        <input type="file" name="file"/><br/>
        <input id="submit" type="submit"/>
    </form>
    
    
    名字:
    
    姓氏:
    电话:
    电邮:
    角色:

    文件:

    @RequestPart依赖于httpmessageconverter和content type将多部分数据绑定到方法param,而@RequestParam依赖于注册的转换器进行转换。SpringMVC默认提供某些转换器。您可以使用@RequestParam或@RequestPart绑定文件数据。大多数应用程序使用commons文件上载来上载文件和注册

    org.springframework.web.multipart.commons.commons多部分解析器

    用于多部件分解。注册后,spring检查每个多部分数据请求,并使用它将其解析为方法arg。检查这里

    您可以尝试以下几项。确保您的验证码和文件参数都是可选的,如控制器中的下面所示。我切换到@RequestParam获取验证码

    @RequestMapping(value = "/careers/pursue", method = RequestMethod.POST)
      public Callable<String> pursue(
          final @RequestParam(value = "g-recaptcha-response", required = false) String captchaResponse,
          final @RequestPart(value = "file", required = false) MultipartFile file,
          final @ModelAttribute("jobapplication") @Valid JobApplication application, final BindingResult bindingResult,
          final Model model) 
    
    @RequestMapping(value=“/careers/purse”,method=RequestMethod.POST)
    公开追诉(
    final@RequestParam(value=“g-recaptcha-response”,required=false)字符串captchaResponse,
    最终@RequestPart(value=“file”,required=false)多部分文件,
    final@modeldattribute(“jobapplication”)@有效的jobapplication应用程序,final BindingResult BindingResult,
    最终模型(模型)
    

    希望这能有所帮助。

    如果任何参数为空,为什么我在控制器方法可以执行之前就得到了异常?表单操作url不是问题,我在编辑问题时忘了更新它。您发布的错误是关于jobapplication bean而不是文件。jobapplication bean是否为空?验证码和文件是jobapplication的一部分吗?如果您查看我发布的多部分数据,如果
    file
    g-response-captcha
    为空,我会得到
    jobapplication
    bean的此错误。这让我很困惑。captcha参数由google captcha附加到请求中,文件和captcha不属于
    jobapplication
    bean@AnadiMisra我把文件作为bean的一部分当我尝试在不选择文件的情况下提交表单时,modelattribute的映射失败。您找到解决方案了吗?
    StandardServletMultipartResolver
    的行为是否与
    commons multipartresolver
    有所不同?它也会有所帮助。它的配置不同。我主要使用公共空间。你在用什么?我在spring boot上,它显然使用
    StandardServletMultipartResolver
    作为默认的多部分解析器。更改为@minion建议的语法,并添加了
    CommonsMultipartResolver
    的配置,但没有帮助,仍然得到相同的结果error@minion我也有同样的问题。请告诉我你们是否找到了解决办法。