使用 Spring-boot、thymeleaf 和 dropzone.js 上传 pdf 时出现 405 错误

Getting 405 error when uploading a pdf using Spring-boot, thymeleaf and dropzone.js

我创建了一个简单的表单,以便将单个 PDF 文件上传到我的数据库中的一个 blob 中。我正在使用 thymeleaf + spring 引导和 dropzone.js。

我收到 405 错误 - 上传文件时不允许使用方法。

我已经尝试在我的控制器方法中添加一个@ResponseBody,但错误仍然存​​在。

这是我的 pdf-upload.html(thymeleaf 模板):

<html>
    <head>
       ...
            <script type="text/javascript">
            Dropzone.options.myAwesomeDropzone = {
        //      url: "[[@{/vouchers/uploadPdfFile}]]",
                paramName : "file", // The name that will be             used to transfer the file
                maxFilesize : 10, // MB
                addRemoveLinks : true,
                acceptedFiles : ".pdf",
                maxFiles : 1,
//              headers: {
//                  'X-CSRF-TOKEN': $('meta[name="token"]').attr('content')
//              }
            };
        </script>

....
</head>

....

<form th:action="@{/vouchers/uploadPdfFile}"
                            enctype="multipart/form-data" 
                            method="post" class="dropzone needsclick dz-clickable"
                            id="my-awesome-dropzone">

                            <div class="col-12" id="dropzone">
                                <div class="dz-message needsclick">
                                    Drop the PDF that will be attached to the Voucher...<br>
                                </div>
                            </div>
                            <input type="hidden" id="hd_voucher_id" th:value="${voucher.id}">
                        </form>


....
</html>

我的控制器看起来像这样:

   @RequestMapping(value = "/uploadPdfFile", method = RequestMethod.POST)
    public String uploadPdfPost(MultipartHttpServletRequest request) {

        try {
            Iterator<String> itr = request.getFileNames();

            while (itr.hasNext()) {
                String uploadedFile = itr.next();
                MultipartFile file = request.getFile(uploadedFile);
                String mimeType = file.getContentType();
                String filename = file.getOriginalFilename();
                byte[] bytes = file.getBytes();

                FileUpload newFile = new FileUpload(filename, bytes, mimeType);

                //fileUploadService.uploadFile(newFile);
            }
        } catch (Exception e) {
            return "vouchers/pdf-upload";
        }
        return "vouchers/pdf-upload";
    }

当提交表单时,我收到了我之前提到的 405 错误。

在日志控制台中,我收到以下错误:

2019-07-16 15:45:27 WARN  o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
2019-07-16 15:45:27 DEBUG o.s.web.servlet.DispatcherServlet - Exiting from "FORWARD" dispatch, status 405
2019-07-16 15:45:27 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
2019-07-16 15:45:27 ERROR o.s.b.w.s.support.ErrorPageFilter - Cannot forward to error page for request [/vouchers/uploadPdfFile] as the response has already been committed. As a result, the response may have the wrong status code. If your application is running on WebSphere Application Server you may be able to resolve this problem by setting com.ibm.ws.webcontainer.invokeFlushAfterService to false

这里有什么提示吗?

提前致谢 娟

我找到了解决方案。我将与社区分享它,因为我花了两天时间解决这个问题,也许这个 post 可以帮助某人...

首先,该问题与 CSRF 生成或 posting 无关。是一个完全不同的问题伪装成 405 错误。这是关于此事的第一个结论:您可能会收到 405 错误,但它可能是由其他错误生成的(内部服务器错误 - 在我的情况下为 500)。

与我收到 500 错误的原因无关,重要的是我如何得出 500 错误显示为 405 错误的结论:我简单地从 spring 启动禁用了 csrf 令牌机制通过将以下行添加到我的应用程序配置中(扩展 WebSecurityConfigurerAdapter 的 class):

http
  .csrf().disable(); // this will disable the crsf token mechanism from the entire app. Be careful!!

当我禁用它时,出现了 500 错误,这是一个非常简单的错误(百里香解析表达式错误)。我解决了500,然后我再次启用csrf机制,它成功了!

希望对以后的人有所帮助。

问候 娟

正如Juan指出的那样,405错误很可能是因为csrf token。 Dropzone 在内部更改请求类型,这会导致 csrf 令牌发生变化,进而导致此异常。为了防止这种情况,您需要做的就是传递与表单中相同的 csrf 令牌。

在您的表单中添加:


    meta name="_csrf" th:content="${_csrf.token}"
    meta name="_csrf_header" th:content="${_csrf.headerName}

并在 js 中添加:

init: function() {
dzClosure = this;
document.getElementById("submit-form-btn").addEventListener("click", function(e) {
    e.preventDefault();
    e.stopPropagation();
    dzClosure.processQueue();
});

this.on("sending", function(data, xhr, formData) {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");    
    xhr.setRequestHeader(header, token);
 });

}