Загрузка файла в приложение AngularJS JSON Spring MVC 400 Неверный запрос Требуемая часть запроса отсутствует

Мы используем AngularJS 1.5.8, Spring 3.2.17, Jackson 2.6.7;

нам нужно реализовать загрузку файлов с помощью объекта JSON DTO. Мы не можем увидеть успешную загрузку файлов с несколькими конфигурациями и подходами, только DTO, поскольку параметр запроса json работает нормально.

Заранее спасибо!

фрагменты bill_payment.html

<form name="billPaymentForm" enctype="multipart/form-data" ng-submit="onSaveIRFBillPayment()">
<!-- some more elements as part of billPaymentDTO -->
<td class="fielddata6">
<input type = "file" file-model = "attachments.depositSlipFile"/>
</td>

app.js

var app = angular.module('app', [ 'ngRoute', 'ngResource', 'billPaymentAppControllers', 'billPaymentAppServices' ]);
...

app.directive('fileModel', ['$parse', function ($parse) {
    return {
       restrict: 'A',
       link: function(scope, element, attrs) {
          var model = $parse(attrs.fileModel);
          var modelSetter = model.assign;

          element.bind('change', function(){
             scope.$apply(function(){
                modelSetter(scope, element[0].files[0]);
             });
          });
       }
    };
}]);

billPaymentControllers.js

var billPaymentAppControllers = angular.module('billPaymentAppControllers', [ 'billPaymentAppServices' ]);

billPaymentAppControllers.controller('billPaymentCtrl',['$routeParams', '$route', '$scope', '$location', '$http', '$window', 'BillPaymentService',  function($routeParams, $route, $scope, $location, $http, $window, BillPaymentService) { 
// many other functions

$scope.onSaveIRFBillPayment =function(){
BillPaymentService.saveBillPayment.saveBillPaymentDtls(
{
    billPaymentDTO : $scope.billPaymentDTO,
    depositSlipFile : $scope.attachments.depositSlipFile
},function(result) {
        console.log(result);
        if(result!=null && result.returnVal!=null && result.returnVal!="" && result.returnVal == "SUCCESS"){
        alert("Bill Payment Saved Successfully");
        } else {
        alert("Error while Saving Bill Payment, please contact IT team");
        return false;
    }
});

}

billPaymentServices.js

billPaymentAppServices.factory('BillPaymentService', function($resource, $http) {
    return{

        saveBillPayment : $resource('agreement/saveIRFBillPayment/', {}, {
            'saveBillPaymentDtls' : {
                method : 'POST',
                headers: {'Content-Type': undefined},
                //transformRequest: angular.identity,                
                transformRequest: function (data) {
                      var formData = new FormData();
                      console.log("data DTO: "+angular.toJson(data.billPaymentDTO));
                      formData.append('billPaymentDTO', angular.toJson(data.billPaymentDTO));                  
                    //console.log("data file Content: "+data.depositSlipFile);
                    //formData.append('billPaymentDTO', new Blob([angular.toJson(data.billPaymentDTO)], {
                    //type: "application/json"
                    //}));
                      formData.append("file", data.depositSlipFile);
                      return formData;
                },
                  transformResponse : function(data) {
                    console.log(data);
                    data = {"returnVal":data};
                    return data;
                }
            }
       })
    }
}

BillPaymentRestController.java

//@ExceptionHandler(Exception.class) 
/*** This signature is working for DTO object alone in request ***/
//@RequestMapping(value="/saveIRFBillPayment", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
//public @ResponseBody String saveIRFBillPayment(@RequestBody String billPaymentDTO,HttpServletRequest request) throws RatingsServiceException, Exception{ 

/*** tried without consumes attribute, without argument HttpServletRequest request ***/
//@RequestMapping(value="/saveIRFBillPayment", method = RequestMethod.POST)
//@ResponseBody String saveIRFBillPayment(@RequestPart("billPaymentDTO") String billPaymentDTO, @RequestPart("file") MultipartFile depositSlipFile) throws RatingsServiceException, Exception{

//public @ResponseBody String saveIRFBillPayment(@RequestParam String billPaymentDTO, @RequestParam("file") MultipartFile depositSlipFile, HttpServletRequest request) throws RatingsServiceException, Exception{

@RequestMapping(value="/saveIRFBillPayment", method = RequestMethod.POST, consumes = {"multipart/form-data"})
@ResponseBody String saveIRFBillPayment(@RequestPart("billPaymentDTO") String billPaymentDTO, HttpServletRequest request, @RequestPart("file") MultipartFile depositSlipFile) throws Exception{

    System.out.println("Data inside saveIRFBillPayment:"+billPaymentDTO);
    System.out.println("\nFile inside saveIRFBillPayment:"+depositSlipFile);
    ObjectMapper mapper = new ObjectMapper();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    mapper.setDateFormat(dateFormat);
    String status="Fail";
    try{
    BillPaymentDTO billPaymentDTOConverted = mapper.readValue(billPaymentDTO, BillPaymentDTO.class);
    File ExtractedDepositSlipFile = billPaymentDTOConverted.getDepositSlipFile();
    System.out.println("File exists Check: "+ExtractedDepositSlipFile.exists());

    //Call to some service
    status="SUCCESS";

    }
    catch (Exception e) {
        e.printStackTrace();
    }

    return status;
}

dipatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="com.xxx.xxxxx.restController.addAgreement" />
    <tx:annotation-driven />
    <mvc:annotation-driven />

    <!--        Added by PV - Type conversion -->
    <bean id="jacksonMessageConverter"
        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>


    <bean
        class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
    <list>
          <ref bean="jacksonMessageConverter" />
    </list>
     </property>
    </bean>

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="268435456" /> <!-- 256 megs -->
    </bean>

    <bean id="methodHandlerExceptionResolver" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
        <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter" />
            <bean class="org.springframework.http.converter.FormHttpMessageConverter" />
            <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
        </list>
        </property>
    </bean>
</beans>

Заголовок запроса:

Request:              POST /RatingsBD/cc-app/agreement/saveIRFBillPayment HTTP/1.1
X-Requested-With:         XMLHttpRequest
Accept:                 application/json, text/plain, */*
Content-Type:  multipart/form-data; boundary=---------------------------7e01c61b10544

Тело запроса:

Blank! when DTO being sent alone, it has json string with boundary

Заголовок ответа:

Response:           HTTP/1.1 400 Bad Request

Тело ответа:

Required request part 'billPaymentDTO' is not present.

person Nishant Soni    schedule 04.10.2016    source источник
comment
formData.append('billPaymentDTO', angular.toJson(data.billPaymentDTO)); не добавляет часть запроса. Пожалуйста, обратитесь к документации для FormData.append().   -  person a better oliver    schedule 04.10.2016
comment
Измените formData.append('billPaymentDTO', angular.toJson(data.billPaymentDTO)); в JSON.stringify(data.billPaymentDTO)   -  person ngCoder    schedule 04.10.2016
comment
мы попытались поместить formData.append('billPaymentDTO', JSON.stringify(data.billPaymentDTO)); вместо formData.append('billPaymentDTO', angular.toJson(data.billPaymentDTO)); все еще получаю те же 400 Bad Request, а тело запроса остается пустым   -  person Nishant Soni    schedule 04.10.2016
comment
Мы заметили, что если только DTO отправляется отдельно, тело запроса имеет следующий вид: -----------------------------7e02552210544 Content-Disposition : данные формы; name=billPaymentDTO {irfBill:{billingClient:{ ....}}} ----------------------------7e02552210544-- еще та же ошибка 400 Требуемая часть запроса 'billPaymentDTO' отсутствует.   -  person Nishant Soni    schedule 04.10.2016
comment
Пожалуйста, не могли бы вы опубликовать свою структуру DTO   -  person ngCoder    schedule 04.10.2016
comment
@NishantSoni опубликовал решение, попробуйте и дайте мне знать, если оно сработает!   -  person ngCoder    schedule 04.10.2016
comment
Привет, к сожалению, это не сработало, возникла та же проблема, структура DTO слишком обширна для публикации и включает множество внутренних объектов и массивов, если отправить только DTO, запрос работает нормально, ошибка возникает только при добавлении файла в запрос FormData или при отправке только файла в запросе   -  person Nishant Soni    schedule 06.10.2016


Ответы (2)


Вы пробовали что-то подобное.

$resource(
  url,
  {},
  {
    upload: {
      method: 'POST',
      headers: {enctype:'multipart/form-data'}
    },
  }
)
person user6947856    schedule 10.10.2016
comment
попробовали тип контента, попробую это и дам вам знать - person Nishant Soni; 10.10.2016

Существует обходной путь для этой проблемы, попробуйте изменить свой вызов $http ниже, добавив все ваши значения billPaymentDTO в данные формы вместе с файлом и аналогичными параметрами на вашем контроллере, включая требуемые параметры.

                   var formData= new FormData();

                   formData.append('id', data.billPaymentDTO.ID);
                   formData.append('contactNumber', data.billPaymentDTO.number);
                   //add all the billPaymentDTO variables as mentioned above
                   formData.append("file", data.depositSlipFile);
                   var uploadUrl = $rootScope.BASE_URL + "agreement/saveIRFBillPayment";
                   $http.post(uploadUrl, formData, {
                       transformRequest: angular.identity,
                       headers: {
                           'Content-Type': undefined,
                           'Accept': 'application/json'
                       }
                   }).success(function(data) {
                   //do something after success
                  });

В вашем контроллере на стороне JAVA

 @RequestMapping(value="/saveIRFBillPayment", method = RequestMethod.POST)
        @ResponseBody String saveIRFBillPayment(HttpServletRequest request, HttpServletResponse response,
                               @RequestParam(value="file") MultipartFile document,
                               @RequestParam("id") Long id,
                               @RequestParam("contactNumber") String contactNumber,
//similar way add all the attributes of billPaymentDTO here as parameter with required datatypes
                              ) { }
person ngCoder    schedule 04.10.2016
comment
@ ng-10 :) к сожалению, это не сработало, возникла та же проблема, если отправить только DTO, запрос работает нормально, ошибка возникает только при добавлении файла в запрос FormData или при отправке только файла в запросе; код выше работал у вас? - person Nishant Soni; 07.10.2016
comment
Да, это определенно работает! Вы реализовали способ, который я показал в фрагменте кода? Можете ли вы опубликовать свой измененный код? - person ngCoder; 07.10.2016