I am working on a file upload controller and I am currently getting the following error when testing in Postman.
{
"timestamp": "2019-04-18T14:53:07.988+0000",
"status": 400,
"error": "Bad Request",
"message": "Required request part 'file' is not present",
"path": "/upload"
}
At the moment my controller is very simple but first I need to overcome this problem.
I have looked at the answers given
[here](upload file springboot Required request part 'file' is not present"upload file springboot Required request part file is not present")!
But unfortunately, anything suggested here did not resolve my problem
Any help with this error would be appreciated
This is my controller:
#Controller
public class UploadController {
#ResponseBody
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public boolean upload(#RequestParam("file") MultipartFile file) throws IOException {
try {
if (!file.isEmpty()) {
return true;
} else {
return false;
}
}
catch(Exception e){
e.printStackTrace();
return false;
}
}
}
It's difficult without knowing how you are sending the data but here's how i solved sending multipart/form-data through a #RestController:
#PostMapping(value = "/foo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
ResponseEntity fileUpload(#Requestparam("bar") LinkedList<MultipartFile> payload) {
MultipartFile file = payload.get(0)
...
Spring just wouldn't accept anything another than a linked list in my case, but that was form-data sent as an Angular2+ FormData object with field name bar.
As you hasve not mentioned your request model, let it be EarningRequest, so know your model data is:
class EarningRequest{
private FilePart file;
//and other data which you want to add.
//add the getter setters also, so that jackson can map the json to this pojo
}
#RestController
public class UploadController {
#PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public boolean upload (#ModelAttribute EarningRequest earningRequest){
//earningRequest contains the file
//you can get the filePart as earningRequest.getFile()
return true;
}
}
In postman under "key" I wasn't setting anything. I needed to set this as 'file'. I previously made the assumption all I had to do was click the drop-down and select file.
I will include below all the updated code & a link to the image which explains this better(I couldn't display image here as reputation < 10)
link to postman Image
#RestController
public class UploadController {
#PostMapping("/upload")
#ResponseBody
public boolean upload(#RequestParam("file") MultipartFile file) {
try{
if(file.isEmpty() ==false){
System.out.println("Successfully Uploaded: "+ file.getOriginalFilename());
return true;
}
else{
System.out.println("ERROR");
return false;
}
}
catch(Exception e){
System.out.println(e);
return false;
}
}
}
Related
I have an issue similar to this one, the solution does not work as I wished however:
Spring MVC how to create controller without return (String) view?
I have a form which should pass the file:
example
And the controller for it:
#PostMapping("/uploadFile")
public #ResponseBody void uploadFile(Model model, #RequestParam("file") MultipartFile multipartFile) throws InterruptedException {
//, RedirectAttributes redirectAttributes) throws InterruptedException {
Reservation reservation=new Reservation( );
fileService.uploadFile( multipartFile );
File file = new File("\\car-rental\\src\\main\\resources\\static\\attachments", multipartFile.getOriginalFilename());
log.info( "name and path " + file.getName() + file.getPath() );
Picname picname=new Picname();
picname.setPicnameAsString(file.getName() );
log.info( "picname file " + picname.getPicnameAsString() );
TimeUnit.SECONDS.sleep(2);
}
}
I want the controller ONLY to perform the logic without returning anything: it returns however an empty page:
empty page returned
How can I make it not returning anything, just staying on the site with the form? The only idea was to set an delay with the .sleep(), but it would be a workaround and I would like to solve it with a cleaner solution
It is the nature of controllers to return a response since you are developing an MVC application which will receive POST requests to the endpoint you specified.
What you can do is declare the controller to be a #RestController which returns a ResponseEntity indicating that everything went OK or any other appropriate response in case some failure happens.
#RestController
public class ControllerClassName{
#PostMapping("/uploadFile")
public ResponseEntity<?> uploadFile(Model model, #RequestParam("file") MultipartFile multipartFile) throws InterruptedException {
try{
// logic
return ResponseEntity.ok().build();
}catch(Exception e){
return ResponseEntity.badRequest().build();
}
}
}
To address your issue you may need to change the return type of your function.
Using a ResponseEntity return type may be more appropriate than using a ResponseBody return type.
I've written a typical spring boot application, now I want to add integration tests to that application.
I've got the following controller and test:
Controller:
#RestController
public class PictureController {
#RequestMapping(value = "/uploadpicture", method = RequestMethod.POST)
public ResponseEntity<VehicleRegistrationData> uploadPicturePost(#RequestPart("userId") String userId, #RequestPart("file") MultipartFile file) {
try {
return ResponseEntity.ok(sPicture.saveAndParsePicture(userId, file));
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
Test:
#Test
public void authorizedGetRequest() throws Exception {
File data = ResourceUtils.getFile(testImageResource);
byte[] bytes = FileUtils.readFileToByteArray(data);
ObjectMapper objectMapper = new ObjectMapper();
MockMultipartFile file = new MockMultipartFile("file", "test.jpg", MediaType.IMAGE_JPEG_VALUE, bytes);
MockMultipartFile userId =
new MockMultipartFile("userId",
"userId",
MediaType.MULTIPART_FORM_DATA_VALUE,
objectMapper.writeValueAsString("123456").getBytes()
);
this.mockMvc.perform(multipart("/uploadPicture")
.file(userId)
.file(file)
.header(API_KEY_HEADER, API_KEY)).andExpect(status().isOk());
}
Testing the controller with the OkHttp3 client on android works seamlessly, but I can't figure out how to make that request work on the MockMvc
I expect 200 as a status code, but get 404 since, I guess, the format is not the correct one for that controller
What am I doing wrong?
It must be a typo.
In your controller, you claim the request URL to be /uploadpicture, but you visit /uploadPicture for unit test.
I have an API built with Spring Boot. By default the default JSON structure when an error is thrown by Spring is;
{
"timestamp": 1477425179601,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/categoriess"
}
This structure is different to error responses returning myself in the API, so I'd like to change Spring to use the same structure as my own for consistency.
My error response are structured like this;
{
"errors": [
{
"code": 999404,
"message": "The resource you were looking for could not be found"
}
]
}
How would I go about doing this? I've tried using an Exception Handler, but I can't figure out the correct exception to set it up for. I'd like to also make sure that the Http status is still correctly returned as 404, or whatever the error is (500 etc).
I had another look at this and did manage to put something together that works for me.
#Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
#Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
Map<String, Object> error = new HashMap<>();
error.put("code", errorAttributes.get("status"));
error.put("message", errorAttributes.get("error"));
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("errors", error);
return errorResponse;
}
};
}
This returns the following JSON response along with whatever header/http status code spring was going to return.
{
"errors": {
"code": 404,
"message": "Not Found"
}
}
This seems to work great for errors generated by spring, while my own Exceptions I'm handling in Controllers or in a specific ControllerAdmin class with ExceptionHandlers.
A possible way to do something like this is to use the #ExceptionHandler annotation to create a handler method inside your controller.
#RestController
#RequestMapping(produces = APPLICATION_JSON_VALUE)
public class MyController {
#RequestMapping(value = "/find", method = GET)
public Object find() {
throw new UnsupportedOperationException("Not implemented yet!");
}
#ExceptionHandler
public ErrorListModel handleException(Exception exception) {
ExceptionModel exceptionModel = new ExceptionModel(1337, exception.getMessage());
ErrorListModel list = new ErrorListModel();
list.add(exceptionModel);
return list;
}
private class ErrorListModel {
private List<ExceptionModel> errors = new ArrayList<>();
public void add(ExceptionModel exception) {
errors.add(exception);
}
public List<ExceptionModel> getErrors() {
return errors;
}
}
private class ExceptionModel {
private int code;
private String message;
public ExceptionModel(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
}
The private classes ErrorListModel and ExceptionModel just help defining how the resulting JSON body should look, and I assume you already have your own, similar classes.
The find method just throws an exception for us to handle, which gets intercepted by the handleException method because it's annotated with #ExceptionHandler. In here, we create an ExceptionModel, populate it with information from the original exception, and add it to an ErrorListModel, which we then return.
This blog post from 2013 explains the features better than I ever could, and it also mentions an additional option, #ControllerAdvice. It basically allows you to re-use the exception handling in other controllers as well.
I have created a springboot application that contains some Rest API endpoints in .../api/myEndpoints... and thymeleaf templates for some UI forms the user can interact with.
Since I added an errorController:
#Controller
#RequestMapping("/error")
public class ErrorController {
#RequestMapping(method = RequestMethod.GET)
public String index(Model model) {
return "error";
}
}
whenever an exception is being thrown in my RestControllers, I receive an empty white website containing the word "error". This maybe makes sense for the web frontend, but not for my api. For the API I want spring to output the standard JSON result e.g.:
{
"timestamp": 1473148776095,
"status": 400,
"error": "Bad request",
"exception": "java.lang.IllegalArgumentException",
"message": "A required parameter is missing (IllegalArgumentException)",
"path": "/api/greet"
}
When I remove the index method from the ErrorController, then I always receive the JSON output.
My question is: Is it somehow possible to exclude the automatic redirection to /error for all api urls (../api/*) only?
Thanks a lot.
There may be a better solution out there, until then... here's how you can achieve what you asked:
(1) Disable ErrorMvcAutoConfiguration
Add this to your application.properties:
spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration
(2) Define two ControllerAdvices
Since we disabled ErrorMvcAutoConfiguration, we need to catch the exception ourself. Create one advice to catch error for a specific package, and another advice to catch all other. They each redirect to a different url.
//Catch exception for API.
#ControllerAdvice(basePackageClasses = YourApiController.class)
#Order(Ordered.HIGHEST_PRECEDENCE)
public static class ErrorApiAdvice {
#ExceptionHandler(Throwable.class)
public String catchApiExceptions(Throwable e) {
return "/error/api";
}
}
//Catch all other exceptions
#ControllerAdvice
#Order(Ordered.LOWEST_PRECEDENCE)
public static class ErrorAdvice {
#ExceptionHandler(Throwable.class)
public String catchOtherExceptions() {
return "/error";
}
}
(3) create a controller to handle the error page
This is where you can have different logic in your error handling:
#RestController
public class MyErrorController {
#RequestMapping("/error/api")
public String name(Throwable e) {
return "api error";
}
#RequestMapping("/error")
public String error() {
return "error";
}
}
With Spring-Boot 1.4.x you can also implement ErrorViewResolver (see this doc):
#Component
public class MyErrorViewResolver implements ErrorViewResolver {
#Override
public ModelAndView resolveErrorView(HttpServletRequest request,
HttpStatus status, Map<String, Object> model) {
if("/one".equals(model.get("path"))){
return new ModelAndView("/errorpage/api");
}else{
return new ModelAndView("/errorpage");
}
}
}
I am using Spring Boot for a simple REST API and would like to return a correct HTTP statuscode if something fails.
#RequestMapping(value="/rawdata/", method = RequestMethod.PUT)
#ResponseBody
#ResponseStatus( HttpStatus.OK )
public RestModel create(#RequestBody String data) {
// code ommitted..
// how do i return a correct status code if something fails?
}
Being new to Spring and Spring Boot, the basic question is how do i return different status codes when something is ok or fails?
There are several options you can use. Quite good way is to use exceptions and class for handling called #ControllerAdvice:
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
Also you can pass HttpServletResponse to controller method and just set response code:
public RestModel create(#RequestBody String data, HttpServletResponse response) {
// response committed...
response.setStatus(HttpServletResponse.SC_ACCEPTED);
}
Please refer to the this great blog post for details: Exception Handling in Spring MVC
NOTE
In Spring MVC using #ResponseBody annotation is redundant - it's already included in #RestController annotation.
One of the way to do this is you can use ResponseEntity as a return object.
#RequestMapping(value="/rawdata/", method = RequestMethod.PUT)
public ResponseEntity<?> create(#RequestBody String data) {
if(everything_fine) {
return new ResponseEntity<>(RestModel, HttpStatus.OK);
} else {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
A nice way is to use Spring's ResponseStatusException
Rather than returning a ResponseEntityor similar you simply throw the ResponseStatusException from the controller with an HttpStatus and cause, for example:
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cause description here");
This results in a response to the client containing the HTTP status:
{
"timestamp": "2020-07-09T04:43:04.695+0000",
"status": 400,
"error": "Bad Request",
"message": "Cause description here",
"path": "/test-api/v1/search"
}
Note: HttpStatus provides many different status codes for your convenience.
In case you want to return a custom defined status code, you can use the ResponseEntity as here:
#RequestMapping(value="/rawdata/", method = RequestMethod.PUT)
public ResponseEntity<?> create(#RequestBody String data) {
int customHttpStatusValue = 499;
Foo foo = bar();
return ResponseEntity.status(customHttpStatusValue).body(foo);
}
The CustomHttpStatusValue could be any integer within or outside of standard HTTP Status Codes.
Try this code:
#RequestMapping(value = "/validate", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<ErrorBean> validateUser(#QueryParam("jsonInput") final String jsonInput) {
int numberHTTPDesired = 400;
ErrorBean responseBean = new ErrorBean();
responseBean.setError("ERROR");
responseBean.setMensaje("Error in validation!");
return new ResponseEntity<ErrorBean>(responseBean, HttpStatus.valueOf(numberHTTPDesired));
}
There are different ways to return status code,
1 : RestController class should extends BaseRest class, in BaseRest class we can handle exception and return expected error codes.
for example :
#RestController
#RequestMapping
class RestController extends BaseRest{
}
#ControllerAdvice
public class BaseRest {
#ExceptionHandler({Exception.class,...})
#ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorModel genericError(HttpServletRequest request,
HttpServletResponse response, Exception exception) {
ErrorModel error = new ErrorModel();
resource.addError("error code", exception.getLocalizedMessage());
return error;
}
I think the easiest way is to make return type of your method as
ResponseEntity<WHATEVER YOU WANT TO RETURN>
and for sending any status code, just add return statement as
return ResponseEntity.status(HTTP STATUS).build();
For example, if you want to return a list of books,
public ResponseEntity<List<books>> getBooks(){
List<books> list = this.bookService.getAllBooks();
if(list.size() <= 0)
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
else
return ResponseEntity.of(Optional.of(list));
}