Angular: как отправить файл на сервер при отправке формы

У меня есть форма, которая публикуется в моем бэкэнде (Kotlin, Spring Web). В этой форме было несколько текстовых вводов, и сообщение работало безупречно. Но когда я добавил ввод файла, пост перестал работать, возвращая следующую ошибку:

{status: 400, error: "Bad Request",…}
error: "Bad Request"
exception: "org.springframework.http.converter.HttpMessageNotReadableException"
message: "Could not read document: No suitable constructor found for type [simple type, class com.test.InsertConfigCommand]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)↵ at [Source: java.io.PushbackInputStream@2cb43211; line: 1, column: 2]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.test.InsertConfigCommand]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)↵ at [Source: java.io.PushbackInputStream@2cb43211; line: 1, column: 2]"

Вот коды моего стека:

Просмотр:

<form ng-submit="insert(config)">
    <input type="text" ng-model="config.name">
    <input type="text" ng-model="config.address">
    <input type="file" ng-model="config.file">
    <button type="submit">Save</button>
</form>

Контроллер (внешний интерфейс):

$scope.insert = function (config) {
    $http.post('/config', config)
        .then(function (response) {
            $.snackbar({content: "Success!"});
        }, $scope.showErrorMessage);
};

Контроллер (внутренняя часть):

@RequestMapping(method = arrayOf(RequestMethod.POST))
fun insert(@RequestBody config: InsertConfigCommand) = service.save(config)

InsertConfigCommand

data class InsertConfigCommand (
    val name : String = "",
    val address : String = "",
    val file : MultipartFile
)

Я попытался сделать сообщение следующим образом, он работает, но только отправляет файл:

Контроллер (внешний интерфейс):

$scope.insert = function (file) {
    var fd = new FormData();
    fd.append('file', file);

    return $http.post('/config', fd, {
        transformRequest: angular.identity,
        headers: {
            'Content-Type': undefined
        }
    });
};

Контроллер (внутренняя часть):

@RequestMapping(method = arrayOf(RequestMethod.POST))
fun insert(@RequestParam(value = "file", required = true) file: MultipartFile) = service.save(file)

Что мне нужно изменить, чтобы этот пост работал? Я хочу отправить входной файл на тот же объект, что и имя и адрес.


person Marcos Tanaka    schedule 01.06.2016    source источник


Ответы (2)


Я полагаю, вы используете Джексона, верно?

Байт-код классов данных в Kotlin выглядит иначе, чем обычный POJO (с конструктором по умолчанию), поэтому Джексон не может инициализировать такой класс. Попробуйте добавить зависимость к модулю Jackson Kotlin и обязательно зарегистрируйте ее.

Если вы используете Spring Boot, добавьте следующий код в любой из ваших аннотированных классов @Configuration:

@Bean
open fun kotlinModule(): KotlinModule {
    return KotlinModule()
}

и посмотрите, поможет ли это.

person Rafal G.    schedule 02.06.2016
comment
Извините, это не сработало. Я не думаю, что это проблема Джексона, потому что, когда я делаю сообщение без параметра файла, синтаксический анализ работает. - person Marcos Tanaka; 02.06.2016

Я использовал этот учебник, который инкапсулирует файл в объект FormData и публикует этот объект https://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs

$scope.insert = function (config) {
    var fd = new FormData();
    fd.append('name', config.name);
    fd.append('address', config.address);
    fd.append('file', $scope.file);
    $http.post('/config', fd, {
            transformRequest: angular.identity,
            headers: {'Content-Type': undefined}
        })
        .then(function (response) {
            $.snackbar({content: "Success!"});
        }, $scope.showErrorMessage);
};

И на моем контроллере Kotlin я получаю каждый атрибут как отдельный параметр:

@RequestMapping(method = arrayOf(RequestMethod.POST))
fun insert(@RequestParam(value = "name", required = true) name: String,
           @RequestParam(value = "address", required = true) address: String,
           @RequestParam(value = "file", required = false) file: MultipartFile): InsertConfigCommand? {

    val command = InsertConfigCommand(
                   name = name,
                   address = address,
                   file = file)

    return service.save(command)
}
person Marcos Tanaka    schedule 02.06.2016