Spring Boot file upload doesn't work (bad request) - java

I'm trying to do a simple file upload via a Rest API created with Spring Boot and Kotlin. This is my code:
#RestController
#RequestMapping("/api")
class Controller {
#PostMapping("/upload")
fun handleFileUpload(#RequestParam("file") file: MultipartFile): ResponseEntity<String> {
try {
file.transferTo(File("C:\\upload\\" + file.originalFilename))
}
catch (e: Exception) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()
}
return ResponseEntity.ok("File uploaded successfully.")
}
}
When I use Postman to test it, I get the status "400 Bad Request".
I'm using a Post-Request with the URL http://localhost:8080/api/upload. In the Header Section I left everything how it is (I read somewhere that the Content-Type Header sometimes causes trouble and therefore turned it off temporarily but this didn't help). In the Body section I chose "form-data", added a key called "file" and selected my test-file as a value.
What am I doing wrong?

Try to check your configuration, in particular if you have in the application.yml or application.properties the following:
spring.servlet.multipart.enabled=true
spring.servlet.multipart.location=${java.io.tmpdir}
And also in your pom.xml or build.gradle the dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.1</version>
</dependency>
You can get some inspiration from this tutorial: https://www.baeldung.com/spring-file-upload

Related

Why SpringDoc OpenAPI doesn't understand MultipartFile payload?

Friends,
I am working on a Spring boot application which has a controller to help upload Multipart files.
#PostMapping("/files")
public ResponseEntity<?> uploadFiles(#RequestParam("file") MultipartFile[] file, String comment) throws IOException, ExecutionException, InterruptedException {
log.debug("Total files to store: {}", file.length);
log.debug("comment: {}", comment);
fileService.storeFile(Arrays.asList(file), comment);
return ResponseEntity.ok(environment.getProperty("file.upload.success"));
}
Problem: Somehow OpenDocAPI (swagger) doesn't understand this payload as file. It shows this field as "string" if I mention #RequestParam("file") MultipartFile file, or "string[ ]" if I use array of MultipartFiles.
My Spring boot parent:
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
The spring doc openapi dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.9</version>
</dependency>
The swagger page:
The result page when I click on "Try it out"
The "Execute" Button doesn't work
Any idea what am I missing in the controller?
PS - I tried with mentioning #PostMapping(value = "/files", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}), still openapi would only treat it as string.

How to decode JSon response with custom Feign client?

In my application, I have to know, from a list a server addresses, which are up. The solution I found is to call health endpoint from Spring-Boot Actuator, for each of them. The JSon reponse is:
{
"status": "UP"
}
In other parts of the application, I use Feign clients from Spring-Cloud defined with the #FeignClient annotation, which works perfectly:
#FeignClient(
name = "tokenProxy",
url = "${host}:${port}"
)
Unfortunately this kind of configuration doesn't allow to re-use the same client to call the same endpoint on different addresses. So I have to define my own custom client (If there is another solution, do not hesitate to tell me! ):
#GetMapping(
value = "/servers"
)
public Server discover() {
MyClient myClient = Feign.builder()
.target(
Target.EmptyTarget.create(
MyClient.class
)
);
return myClient.internalPing(URI.create("http://localhost:8090"));
}
interface MyClient {
#RequestLine("GET /actuator/health")
Server internalPing(URI baseUrl);
}
class Server {
private final String status;
#JsonCreator
public Server(#JsonProperty("status") String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
When I call the endpoint /servers, I get the following error, indicating that my custom Feign client isn't confgured with the appropriate decoder:
feign.codec.DecodeException: class com.xxx.web.Server is not a type supported by this decoder.
at feign.codec.StringDecoder.decode(StringDecoder.java:34) ~[feign-core-10.10.1.jar:na]
at feign.codec.Decoder$Default.decode(Decoder.java:92) ~[feign-core-10.10.1.jar:na]
at feign.AsyncResponseHandler.decode(AsyncResponseHandler.java:115) ~[feign-core-10.10.1.jar:na]
at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:87) ~[feign-core-10.10.1.jar:na]
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138) ~[feign-core-10.10.1.jar:na]
I guess i should use JacksonDecoder, but I cannot find it in my dependencies from Spring-Cloud Hoxton.SR5:
<dependencies>
...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
...
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
Could someone help me, either with a better solution for my need or a explanation on how to properly configure custom Feign clients?
Thanks in advance
In fact, the library which includes Jackson decoder and encoder was not loaded by default when using spring-cloud dependencies. To fix the issue I simply had to add the following to my pom.xml file:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
</dependency>
Another way could be annotating the class with #Import(FeignClientsConfiguration.class) which is the default configuration provided by Spring Cloud Netflix.
Then it becomes easy to inject both Encoder and Decoder when creating the Feign client:
#Import(FeignClientsConfiguration.class)
#Configuration
public class MyConfiguration {
(...)
Myclient myClient (Decoder feignDecoder, Encoder feignEncoder) {
return Feign.builder()
.decoder( feignDecoder )
.encoder( feignEncoder )
.target(
Target.EmptyTarget.create(
MyClient.class
)
);
}
There are two different defined encoders in the configuration class (pageable or not), so pay attention to clearly identify which you want, either by its name or a qualifier.

Cannot send http post request to springboot

When I send an HTTP post request to spring boot rest API from my angular application, request is failing with below error
Browser Error
HTTP Status 415 – Unsupported Media Type
Type Status Report
Description: The origin server is refusing to service the request because the payload is in a format
not supported by this method on the target resource
Spring boot console error
java.lang.IllegalArgumentException: No converter found for return value of type: class java.util.LinkedHashMap
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:187) ~[spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:203) ~[spring-webmvc-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
.......
What I have tried so far
As this solution mentioned, i have added the necessary headers to the request from angular end
this.http.post(ipUrl, args, { headers: new HttpHeaders({'Content-Type': 'application/json', 'Accept': 'application/json', 'Access-Control-Allow-Headers': 'Content-Type'})});
As this answer, I have added getters/setters to the Model objects
I want to know where i went wrong and how to resolve this issue?
UPDATE
Springboot Rest Controller method
#PostMapping("/login")
public #ResponseBody ResponseWrapper<WebUser> login(#RequestBody LoginData loginData){
try {
return loginService.loginProcess(loginData);
}
catch (Exception ex){
ProgrammerAlert.printStackTrace(ex);
return new ResponseWrapper<>(ResponseWrapper.ERROR, ex.getMessage());
}
}
Could you write your controller in this way. and see if it responds.
#ResponseBody
#RequestMapping(value = "/login",
method = RequestMethod.POST)
public ResponseWrapper<WebUser> login(...){
.
.
.
}
Is there a reason that you do not want to use RequestMapping ?
Is there any reasons not to add produces and consumes properties?
#PostMapping("/login", produces = MediaType.APPLICATION_JSON_UTF8, consumes = MediaType.APPLICATION_JSON_UTF8)
public #ResponseBody ResponseWrapper<WebUser> login(#RequestBody LoginData loginData){
try {
return loginService.loginProcess(loginData);
}
catch (Exception ex){
ProgrammerAlert.printStackTrace(ex);
return new ResponseWrapper<>(ResponseWrapper.ERROR, ex.getMessage());
}
}
or,
Did you add any JSON message converter, like jackson? Check your application has below dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
your return object should be converted to JSON properly. Add Jackson to you pom (separately from starter web) and retry.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
As per your recent edit, you're now getting 406 Not Acceptable, to fix this error, keep the media type of your Spring boot application's response same as the Accept header of your client. Try the following.
Change the value of produces to MediaType.APPLICATION_JSON as you have accept header in client as "application/json". Also please note:
APPLICATION_JSON_UTF8 is deprecated in favor of APPLICATION_JSON
Reference: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/MediaType.html
For those who are facing a similar issue, you can also check if there is any typo in Accept header as I often face this problem.
I also came across the same issue, I guess the answer of this question is very clear
HTTP Status 415 – Unsupported Media Type
while sending the request make the content type json instead of text

How to create APIs that accept file in the request object using jersey?

I'm creating apis that needs to accept a file and other informations which will be sent in a createAppRequest. What should I need to do to my apis to be able to let the user upload a file through the apis.
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createApp(CreateAppRequest){
// save app to db
}
Request class:
public class CreateAppRequest{
// Other fields like name, createDate
#JsonProperty("file")
#Property("file")
private byte [] file;
public byte[] getFile() {
return file;
}
public void setFile(byte[] file) {
this.file = file;
}
}
I'll assume you're using the latest jersey release (2.7).
First you need to enable the MultiPart support in Jersey by adding the following to your pom.xml (if you are using maven, if not add the dependency to your project the same way you have added jersey):
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.7</version>
</dependency>
MultiPart is a Jersey Feature (such as the Jackson feature for example) and this means you will have to register it with both your client (if you have one) and your server apps.
Client side example (optional):
final Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class)
.build();
Server side example:
final Application application = new ResourceConfig()
.packages("your.root.package.here")
.register(MultiPartFeature.class)
Once you've done all of the above you can define your post method like:
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA_TYPE)
public Response createApp(
#DefaultValue("true") #FormDataParam("enabled") boolean enabled,
#FormDataParam("data") FileData bean,
#FormDataParam("file") InputStream file,
#FormDataParam("file") FormDataContentDisposition fileDisposition) {
// your code here
}
For more information and examples take a look at the official jersey docs - https://jersey.java.net/documentation/latest/user-guide.html#multipart
However if you find this whole procedure too complicated you can always put your file in the request body as application/octet-stream and then read it in your post method with a MessageBodyReader<T>. If you are not sure what all these mean, or how to use them, again, check the jersey docs :)

Post data Spring web services Restful

I am trying to save data on my database with a web service POST wich serializes a HTML form to save a object. The rest client firefox says this:
"The server refused this request because the requested entity is in a format not supported by the requested resource for the requested method"
The eclipse console shows the message:
org.jasig.cas.client.util.CommonUtils - safeGetParameter called on a POST HttpServletRequest for LogoutRequest. Cannot complete check safely. Reverting to standard behavior for this Parameter
I understand that the object that i want to save is not valid, but I don't see what the problem is.
#RequestMapping(value="/solicitudCita", method = RequestMethod.POST)
public #ResponseBody String putSolicitud(#ModelAttribute("Solicitud") Solicitud solicitud) throws Exception{
System.out.println(solicitud.toString());
solicitudCitaAppMService.createOrUpdate(solicitud);
String solicitudAdded = "Solicitud de cita -> {" + solicitud.toString() + "} añadida";
System.out.println(solicitud);
return solicitudAdded;
}
Help me please
Thanks
If you want to call this controller in a RESTful manner, you have to annotate the solicitud parameter as #RequestBody. Second, you have to have the Jackson libraries in you classpath so Spring can pick them up and use them for unmarshalling the object.
If you use Maven, use these dependencies:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
BTW, why do you have to serialize the HTML form and sent it across? I would suggest you use a REST client, for instance this one, that is available in the Chrome WebStore.

Categories