Uploading files Kotlin Spring Boot - java

So, I am trying to make and upload file(s) functionality, from some blog posts and stackoverflow questions I got this solution:
#PostMapping("/file/upload")
fun handleFileUpload(
#RequestParam("files") files: List<MultipartFile>?,
): String {
if(files == null) {
return "File is null"
}
// SOME LOGIC
return "All Good it seems"
}
But when I send request trough postman I my files are null, here is postman request:
I saw some some solutions that use #RequestPart instead of #RequestParam, but when I go down that path, I get unsupported media type response.
PS: I use Java 11 and Spring Boot 2.6
UPDATE:
Applied suggestions from comments and ended up with this code:
#PostMapping("/file/upload", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
fun handleFileUpload(
#RequestPart("files") files: List<MultipartFile>?,
): String {
//SOME CODE
}
When I send request trough postman I get 415 Unsupported Media Type

You should use #RequestPart List<MultipartFile> and directly specify Content-Type in Postman
Postman screenshot

Adding to Revasha's response, you should also add
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
on your endpoint to avoid the 415 http error

Related

Request body is being printed out in the console but not the response

I am trying to build a Rest API automation based on Java, RestAssured, and Cucumber. I ' trying to hit an endpoint via POST. The problem is when I am converting the response as string and when I print the response, it is printing the XML file contents but not the response. I also see the status code as 200. I'm not sure what is going wrong in here. Below is the sample snippet from my codebase.
I am trying to hit a WebService (SOAP WSDL).
// required imports
public class Service_Steps {
Response xmlResponse;
#When("I create a POST request for RCP for endpoint using XML")
public void i_create_a_post_request_for_endpoint_using_xml() {
// xml Request body
String path = "pah_to_xml_file";
File file = new File(path);
xmlResponse = RestAssured.given().when().relaxedHTTPSValidation().accept(ContentType.XML).header(<headers>)
.body(file)
.post(url);
String xmlResponseAsString = xmlResponse.then().extract().body().asString();
}
Not sure why I am seeing this ambiguity. Sometimes it is printing the response, and sometimes it is printing the XML file (request body) contents.
After checking with developers I came to know that the SOAP EndPoint is sending out the responses in two different ways, randomly!
Try this one:
xmlResponse = RestAssured.given().log().all().when().relaxedHTTPSValidation().accept(ContentType.XML).header(<headers>)
.body(file)
.post(url)
.then()
.log().all()
.extract()
.response();
This will print out all the request & response stuff

How can I upload a picture in a server using Android and Retrofit?

I am trying to upload a picture in a server with Retrofit but I get HTTP 400 error. I know that this error mean that the call has not been done correctly but I do dont know when I am doing wrong. I only need to upload a single image.
I have tried adding headers multipart/form-data and also content-type: image/jpeg and also without but it keeps throwing 400 error.
This code in the interface:
#Multipart
#POST("https://myweb.com/endpoint")
Observable<DefaultResponse> uploadUserImageRx(
#Part MultipartBody.Part image
);
And here the call:
var file = // initialized file here
var filePart = MultipartBody.Part.createFormData("file",file.getName(), RequestBody.create(MediaType.parse("image/jpeg"), file))
unsubscribeUploadPhoto()
uploadPhotoSubscription = MyApiClient.getApi()
.uploadUserImageRx(filePart)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : SubscriberAdapter<DefaultResponse>()
{
//Notify everything went well.
})
I expect the photo to be upload but i get this error instead.
The requirements of the endpoint are:
Authorization(header) as String
This error mean that you didn,t attached headers correctly as I think there should be a header like access token. check if there is something like that then implement your API like below.
#Multipart
#POST("https://myweb.com/endpoint")
Observable<DefaultResponse> uploadUserImageRx(
#Header("Authorization") String authorization,
#Part MultipartBody.Part image
);

HttpClientErrorException 406 null is thrown in rest template spring mvc [duplicate]

In my Ruby on Rails application I tried to upload an image through the POSTMAN REST client in Base64 format. When I POST the image I am getting a 406 Not Acceptable Response. When I checked my database, the image was there and was successfully saved.
What is the reason for this error, is there anything I need to specify in my header?
My request:
URL --- http://localhost:3000/exercises.json
Header:
Content-Type - application/json
Raw data:
{
"exercise": {
"subbodypart_ids": [
"1",
"2"
],
"name": "Exercise14"
},
"image_file_name": "Pressurebar Above.jpg",
"image":"******base64 Format*******"
}
Your operation did not fail.
Your backend service is saying that the response type it is returning is not provided in the Accept HTTP header in your Client request.
Ref: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
Find out the response (content type) returned by Service.
Provide this (content type) in your request Accept header.
http://en.wikipedia.org/wiki/HTTP_status_code -> 406
406 Not Acceptable
The resource identified by the request is only capable of generating response entities which have content characteristics not
acceptable according to the accept headers sent in the request.
406 happens when the server cannot respond with the accept-header specified in the request.
In your case it seems application/json for the response may not be acceptable to the server.
You mentioned you're using Ruby on Rails as a backend. You didn't post the code for the relevant method, but my guess is that it looks something like this:
def create
post = Post.create params[:post]
respond_to do |format|
format.json { render :json => post }
end
end
Change it to:
def create
post = Post.create params[:post])
render :json => post
end
And it will solve your problem. It worked for me :)
"Sometimes" this can mean that the server had an internal error, and wanted to respond with an error message (ex: 500 with JSON payload) but since the request headers didn't say it accepted JSON, it returns a 406 instead. Go figure. (in this case: spring boot webapp).
In which case, your operation did fail. But the failure message was obscured by another.
You can also receive a 406 response when invalid cookies are stored or referenced in the browser - for example, when running a Rails server in Dev mode locally.
If you happened to run two different projects on the same port, the browser might reference a cookie from a different localhost session.
This has happened to me...tripped me up for a minute. Looking in browser > Developer Mode > Network showed it.
const request = require('request');
const headers = {
'Accept': '*/*',
'User-Agent': 'request',
};
const options = {
url: "https://example.com/users/6",
headers: headers
};
request.get(options, (error, response, body) => {
console.log(response.body);
});
Changing header to Accept: */* resolved my issue and make sure you don't have any other Accept Header
In my case, I added:
Content-Type: application/x-www-form-urlencoded
solved my problem completely.
If you are using 'request.js' you might use the following:
var options = {
url: 'localhost',
method: 'GET',
headers:{
Accept: '*/*'
}
}
request(options, function (error, response, body) {
...
})
In my case for a API in .NET-Core, the api is set to work with XML (by default is set to response with JSON), so I add this annotation in my Controller :
[Produces("application/xml")]
public class MyController : ControllerBase {...}
Thank you for putting me on the path !
It could also be due to a firewall blocking the request. In my case the request payload contained string properties - "like %abc%" and ampersand symbol "&" - which caused the firewall to think it is a security risk (eg. a sql injection attack) and it blocked the request. Note here the request does not actually go to the server but is returned at the firewall level itself.
In my case, there were no application server logs generated so I knew that the request did not actually reach the server and was blocked before that. The logs that helped me were Web application firewall (WAF) logs.

File download using RESTful gives HTTP 406 Not Applicable Error

I'm trying to download an Excel file using RESTful call. I thought it should be pretty simple, but I keep getting HTTP 406 Not Applicable Eror. This is what my controller method looks like:
#RequestMapping(value = "/gettemplate", method = RequestMethod.GET, produces = "application/vnd.ms-excel")
#ResponseBody
public Response getExcelTemplate() throws Exception {
File templateFile = new File("TestFile.xlsx");
ResponseBuilder builder = Response.ok(templateFile,"application/vnd.ms-excel");
builder.header("Content-Disposition", "attachment; filename=\"" + templateFile.getName() + "\"" );
return builder.build();
}
I've tried setting request header to accept application/vnd.ms-excel, I've also tried using application/octet-stream instead of vnd.ms-excel. I get an HTML response back with 406 error message in either case. Here's what my Ajax test call looks like:
Ext.Ajax.request({
url: 'myservice/gettemplate',
//dataType: 'application/vnd.ms-excel',
headers: {
'Accept': 'application/vnd.ms-excel, text/plain, */*'
//'Content-Type': 'application/vnd.ms-excel'
},
success: function (response, options) {
alert(1);
},
failure: function (response, options) {
alert(2);
}
});
I've commented out the lines that I've tried and removed as it didn't help. This could be a very simple config change, but I can't seem to figure out.
When you annotate a request mapping return type with #ResponseBody, Spring will attempt to convert the object into the response using an HttpMessageConverter, as it says on the reference page:
#ResponseBody As with #RequestBody, Spring converts the returned object to a
response body by using an HttpMessageConverter.
You can see the list of available converters here: Message converters
It looks like the application/vnd.ms-excel you specify is not supported by any of the converters. Maybe that is why you get a 406.
406 Not Acceptable
The requested resource is only capable of generating content not acceptable according to the Accept headers sent
in the request.
The solution in your case is to remove the #ResponseBody annotation and handle the file download in another way.
See here for examples on how to download a file from a Spring controller:
Downloading a file from Spring controllers
Returning a file from a controller in Spring
Somewhat based on ESala's answer I tried a few things. Finally got it working by setting header and content type to HttpServletResponse. Here's the code that worked for me. This works in both IE and Firefox flawlessly with just a simple anchor tag, without even an ajax call. Hope it helps someone else.
#RequestMapping(value ="/gettemplate", method = RequestMethod.GET)
#ResponseBody
public void getExcelTemplate(HttpServletRequest request, HttpServletResponse response) throws Exception{
response.setContentType("application/vnd.ms-excel");
response.setHeader("content-disposition", "attachment; filename=TestFile.xlsx");
InputStream fis = getClass().getClassLoader().getResourceAsStream("templates/TestFile.xlsx");
int x = fis.available();
byte byteArray[] = new byte[x];
logger.info(" File size :"+byteArray.length);
fis.read(byteArray);
response.getOutputStream().write(byteArray);
response.flushBuffer();
fis.close();
}

Spring: JSON data and file in the same request

I know how to create endpoints that are handling files using MediaType.MULTIPART_FORM_DATA and #FormDataParam("file") FormDataBodyPart bodyPart, but I was wondering if I can also have JSON data along that request? Something like:
#POST
#Path("somepath")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(#RequestBody SomeModel someModel,
#FormDataParam("file") FormDataBodyPart bodyPart) {
return null;
}
At the moment if I add some JSON data on the "raw" tab on the following Postman request I'm getting HTTP 415 Unsupported Media Type probably because I specified that I consume MULTIPART_FORM_DATA but I'm also using #RequestBody which is looking for JSON content which is APPLICATION_JSON. So how can I have JSON data and a file handled in the same request? I know that it's possible to do that in two requests, I just want to do it in one if possible?
Why are you using both Spring and Jersey annotations? You should stick to using the annotations meant for the framework. Since you are using Jersey, should stick to its its annotations.
So here are the things to consider about your current code and environment.
There can't be two separate bodies. With your code, that's what it appears you expect to happen.
You can though put the JSON as part of the multi-part body. For that you should also annotate the SomeModel with the Jersey #FormDataParam
#POST
#Path("somepath")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(
#FormDataParam("model") SomeModel someModel,
#FormDataParam("file") FormDataBodyPart bodyPart) {
}
In the Jersey configuration, you need to make sure to register the MultiPartFeature. If you don't the body won't be able to be deserialized, and you will get exceptions and error responses.
Now the Postman problem. You can see similar problem here. The problem was that the Content-Type was not set for the JSON body part. For example the body might look something like
--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--
You can actually see the body, if you hit the Preview button in Postman. The problem is that there is no Content-Type for the "model" part, as you can see in the "file" part. This happens because you can't set individual parts' Content-Type in Postman. The one that you will see will be discovered from the file extension. For example a .txt file will make Postman set the Content-Type to text/plain and a .png file to image/png.
If you look in the link above, I proposed maybe you could use a .json file instead of typing in the data. Of course that was just a theory. I didn't actually test it.
In any case, the Content-Type must be set in order for Jersey to be able to know to deserialize it as JSON. If the .json file extension theory doesn't pan out, then you can use a different client, like cURL, which I showed an example in the link, or you can use the Jersey client to test, as seen here.
Don't set the Content-Type header to multipart/form-data in Postman. It sets it for you when you use the form-data. I just saw a post where someone said there is bug when you set the header. Can't find the post now, and not something I've confirmed, but I'd just leave it out.
UPDATE
So the OP was able to find a way to set the Content-Type: application/json to the "model" part. But it is sometimes the case where with a Javascript client, you are not able to set it. So there will be no Content-Type. If this is the case, Jersey will not be able to deserialize the JSON, as it has no idea that it is actually JSON being sent. If you absolutely can't or have no idea how to set the Content-Type for individual parts, you could resort to doing the following.
#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);
}
Yes, you can get that as multipart form data.
you get like this in 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;
}
Have a look at it for angularjs code:
Angularjs how to upload multipart form data and a file?
https://puspendu.wordpress.com/2012/08/23/restful-webservice-file-upload-with-jersey/

Categories

Resources