Spring: JSON 数据和文件在同一个请求中
Spring: JSON data and file in the same request
我知道如何使用 MediaType.MULTIPART_FORM_DATA
和 @FormDataParam("file") FormDataBodyPart bodyPart
创建处理文件的端点,但我想知道我是否也可以在该请求中包含 JSON 数据?类似于:
@POST
@Path("somepath")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@RequestBody SomeModel someModel,
@FormDataParam("file") FormDataBodyPart bodyPart) {
return null;
}
目前,如果我在以下 Postman 请求的 "raw" 选项卡上添加一些 JSON 数据,我得到 HTTP 415 Unsupported Media Type
可能是因为我指定我消耗 MULTIPART_FORM_DATA
但我也在使用 @RequestBody
,它正在寻找 JSON 内容,即 APPLICATION_JSON
。那么如何在同一个请求中处理 JSON 数据和文件呢?我知道可以在两个请求中执行此操作,如果可能的话,我只想在一个请求中执行此操作?
是的,您可以将其作为多部分表单数据获取。
你在 angularjs 中会变成这样:
$scope.uploadFile = function () {
var file = $scope.selectedFile[0];
$scope.upload = $upload.upload({
url: 'api/upload',
method: 'POST',
data: angular.toJson($scope.model),
file: file
}).progress(function (evt) {
$scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total, 10);
}).success(function (data) {
//do something
});
};
$scope.onFileSelect = function ($files) {
$scope.uploadProgress = 0;
$scope.selectedFile = $files;
};
public Response uploadFileAndJSON(@RequestParam("data") String data,
@MultiPartFile("file")File file) {
you can data as form data and convert it
like you want to your object using Gson jar.
return null;
}
查看 angularjs 代码:
Angularjs how to upload multipart form data and a file?
https://puspendu.wordpress.com/2012/08/23/restful-webservice-file-upload-with-jersey/
为什么要同时使用 Spring 和 Jersey 注释?您应该坚持使用适用于框架的注释。由于您使用的是 Jersey,因此应坚持其 its 注释。
下面是您当前的代码和环境需要考虑的事项。
- 不能有两个独立的身体。使用您的代码,这就是您希望发生的情况。
您 可以 尽管将 JSON 作为 multi-part body 的一部分。为此,您还应该使用 Jersey @FormDataParam
注释 SomeModel
@POST
@Path("somepath")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(
@FormDataParam("model") SomeModel someModel,
@FormDataParam("file") FormDataBodyPart bodyPart) {
}
在 Jersey 配置中,您需要确保注册 MultiPartFeature
。如果不这样做,body 将无法反序列化,您将得到异常和错误响应。
现在是 Postman 问题。你可以看到类似的问题here。问题是 Content-Type
没有为 JSON body 部分设置。例如 body 可能看起来像
--AaB03x
Content-Disposition: form-data; name="model"
{"some":"model", "data":"blah"}
--AaB03x
Content-Disposition: form-data; name="file"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
如果您在 Postman 中点击 Preview 按钮,您实际上可以看到 body。问题是 "model"
部分没有 Content-Type
,正如您在 "file"
部分中看到的那样。发生这种情况是因为您无法在 Postman 中设置各个部分的 Content-Type
。您将看到的那个将从文件扩展名中发现。例如,.txt
文件将使 Postman 将 Content-Type
设置为 text/plain
,将 .png
文件设置为 image/png
。
如果您查看上面的 link,我建议您可以使用 .json
文件而不是键入数据。当然那只是一个理论。我没有实际测试它。
在任何情况下,必须设置 Content-Type
以便 Jersey 能够知道将其反序列化为 JSON。如果 .json
文件扩展理论没有成功,那么你可以使用不同的客户端,比如 cURL,我在 link 中展示了一个例子,或者你可以使用 Jersey 客户端来测试,如所见 here.
请勿在 Postman 中将 Content-Type
header 设置为 multipart/form-data
。它会在您使用 form-data 时为您设置。我刚看到 post 有人说当你设置 header 时有错误。现在找不到 post,也不是我已经确认的东西,但我会把它留在外面。
更新
因此 OP 能够找到一种方法将 Content-Type: application/json
设置为 "model" 部分。但有时使用 Javascript 客户端时,您无法设置它。所以不会有Content-Type
。如果是这种情况,Jersey 将无法反序列化 JSON,因为它不知道它实际上是 JSON 被发送。如果您绝对不能或不知道如何为各个部分设置 Content-Type
,您可以采取以下措施。
@POST
@Path("somepath")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@FormDataParam("model") FormDataBodyPart jsonPart,
@FormDataParam("file") FormDataBodyPart bodyPart) {
jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
SomeModel model = jsonPart.getValueAs(SomeModel.class);
}
我知道如何使用 MediaType.MULTIPART_FORM_DATA
和 @FormDataParam("file") FormDataBodyPart bodyPart
创建处理文件的端点,但我想知道我是否也可以在该请求中包含 JSON 数据?类似于:
@POST
@Path("somepath")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@RequestBody SomeModel someModel,
@FormDataParam("file") FormDataBodyPart bodyPart) {
return null;
}
目前,如果我在以下 Postman 请求的 "raw" 选项卡上添加一些 JSON 数据,我得到 HTTP 415 Unsupported Media Type
可能是因为我指定我消耗 MULTIPART_FORM_DATA
但我也在使用 @RequestBody
,它正在寻找 JSON 内容,即 APPLICATION_JSON
。那么如何在同一个请求中处理 JSON 数据和文件呢?我知道可以在两个请求中执行此操作,如果可能的话,我只想在一个请求中执行此操作?
是的,您可以将其作为多部分表单数据获取。
你在 angularjs 中会变成这样:
$scope.uploadFile = function () {
var file = $scope.selectedFile[0];
$scope.upload = $upload.upload({
url: 'api/upload',
method: 'POST',
data: angular.toJson($scope.model),
file: file
}).progress(function (evt) {
$scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total, 10);
}).success(function (data) {
//do something
});
};
$scope.onFileSelect = function ($files) {
$scope.uploadProgress = 0;
$scope.selectedFile = $files;
};
public Response uploadFileAndJSON(@RequestParam("data") String data,
@MultiPartFile("file")File file) {
you can data as form data and convert it
like you want to your object using Gson jar.
return null;
}
查看 angularjs 代码: Angularjs how to upload multipart form data and a file?
https://puspendu.wordpress.com/2012/08/23/restful-webservice-file-upload-with-jersey/
为什么要同时使用 Spring 和 Jersey 注释?您应该坚持使用适用于框架的注释。由于您使用的是 Jersey,因此应坚持其 its 注释。
下面是您当前的代码和环境需要考虑的事项。
- 不能有两个独立的身体。使用您的代码,这就是您希望发生的情况。
您 可以 尽管将 JSON 作为 multi-part body 的一部分。为此,您还应该使用 Jersey
注释@FormDataParam
SomeModel
@POST @Path("somepath") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFileAndJSON( @FormDataParam("model") SomeModel someModel, @FormDataParam("file") FormDataBodyPart bodyPart) { }
在 Jersey 配置中,您需要确保注册
MultiPartFeature
。如果不这样做,body 将无法反序列化,您将得到异常和错误响应。现在是 Postman 问题。你可以看到类似的问题here。问题是
Content-Type
没有为 JSON body 部分设置。例如 body 可能看起来像--AaB03x Content-Disposition: form-data; name="model" {"some":"model", "data":"blah"} --AaB03x Content-Disposition: form-data; name="file"; filename="file1.txt" Content-Type: text/plain ... contents of file1.txt ... --AaB03x--
如果您在 Postman 中点击 Preview 按钮,您实际上可以看到 body。问题是
"model"
部分没有Content-Type
,正如您在"file"
部分中看到的那样。发生这种情况是因为您无法在 Postman 中设置各个部分的Content-Type
。您将看到的那个将从文件扩展名中发现。例如,.txt
文件将使 Postman 将Content-Type
设置为text/plain
,将.png
文件设置为image/png
。如果您查看上面的 link,我建议您可以使用
.json
文件而不是键入数据。当然那只是一个理论。我没有实际测试它。在任何情况下,必须设置
Content-Type
以便 Jersey 能够知道将其反序列化为 JSON。如果.json
文件扩展理论没有成功,那么你可以使用不同的客户端,比如 cURL,我在 link 中展示了一个例子,或者你可以使用 Jersey 客户端来测试,如所见 here.请勿在 Postman 中将
Content-Type
header 设置为multipart/form-data
。它会在您使用 form-data 时为您设置。我刚看到 post 有人说当你设置 header 时有错误。现在找不到 post,也不是我已经确认的东西,但我会把它留在外面。
更新
因此 OP 能够找到一种方法将 Content-Type: application/json
设置为 "model" 部分。但有时使用 Javascript 客户端时,您无法设置它。所以不会有Content-Type
。如果是这种情况,Jersey 将无法反序列化 JSON,因为它不知道它实际上是 JSON 被发送。如果您绝对不能或不知道如何为各个部分设置 Content-Type
,您可以采取以下措施。
@POST
@Path("somepath")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@FormDataParam("model") FormDataBodyPart jsonPart,
@FormDataParam("file") FormDataBodyPart bodyPart) {
jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
SomeModel model = jsonPart.getValueAs(SomeModel.class);
}