CSV on RequestBody using Spring - java

I need to receive a csv file on the request and process it.
#PostMapping(value = "/csv", consumes = "text/csv")
public ViewObject postCsv(#RequestBody InputStream request){
// do stuff
}
But when I execute:
curl -X POST -H 'Content-Type: text/csv' -d #input.csv http://localhost:8080/csv
This is what shows up on my console:
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/csv;charset=UTF-8' not supported]
Spring is saying my request is invalid before anything else.
So, the question is: How to properly receive this csv?

As someone mentioned in comments, one approach is to implement your own HttpMessageConverter - there are examples of varying quality available on the web.
Another option if you already have some code for parsing CSVs using Spring's org.springframework.core.io.Resource classes is to change your method signature as follows to use Spring's built in ResourceHttpMessageConverter:
#PostMapping(path = "/csv", consumes = "application/octet-stream")
public ViewObject postCsv(#RequestBody Resource resource){
// do stuff
}

Related

Http Put request with content type application/x-www-form-urlencoded not working in Spring

I have a spring-boot application that the only thing it does is receive http requests.
This is my spring cotroller:
#RestController
public class WebController {
#Autowired
private CallRecording callRecording;
#PutMapping(path = "/cdrpostbox/callrecording/{hostedAccountId}/{mp3FileName}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<Object> callRecording(#PathVariable("hostedAccountId") String hostedAccountId, #PathVariable("mp3FileName") String mp3FileName, MultipartFile file) {
return ResponseEntity.status(callRecording.service(hostedAccountId, mp3FileName, file)).body(null);
}
}
I'm using Postman to send the request. The request that my spring application receives can't be changed, because the code is not maintained by my team.
request headers
request body
I found quite a few questions in here about the similar problems, but not quite the same problem that I have to solve. I tried adding and removing #RequestBody , replacing #RequestBody with #RequestParam, I tried the MultiValueMap, but it keeps returning the same error.
java.lang.IllegalArgumentException: URLDecoder: Illegal hex characters in escape (%) pattern - For input string: "x"
I can't even debug the code because it fails before it reaches the controller.
What am I missing?
The content-type in the header is urlencoded, but you send binary data.
--- edit
If you want to get the file as a MultipartFile, then send the file as a form-data from postman, set the name of the data to file and add #RequestParam to the file argument in your method.
After a bit more investigation, I found the solution.
#RestController
#EnableWebMvc
public class WebController {
#Autowired
private CallRecording callRecording;
#PutMapping(path = "/cdrpostbox/callrecording/{hostedAccountId}/{mp3FileName:.+}")
public ResponseEntity<Object> callRecording(#PathVariable("hostedAccountId") String hostedAccountId, #PathVariable("mp3FileName") String mp3FileName, #RequestBody byte[] requestBody) {
return ResponseEntity.status(callRecording.service(hostedAccountId, mp3FileName, requestBody)).body(null);
}
It was missing the #EnableWebMvc. I also changed the mapping {mp3FileName:.+} because it was truncating the end of the request, you can check the SO question here .
Thanks #Selindek for the help

Why JAX-RS POST does not work with multivalued map

I have JAX-RS 2.8.9 with Spring 4.3.4 app. I perform a very simple POST request to the following server code
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response test(MultivaluedMap<String, String> work) {
return Response.ok(work.keySet().size()).build();
}
I test with curl:
curl -i -X POST 'http://localhost:XXX/some/test' -d "param=value&param2=value2" -H "Content-Type: application/x-www-form-urlencoded"
I get the following warning
A servlet request to the URI http://localhost:XXX/some/test contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
About which I found only cause that involve connection issues, apparently I don't have.
According to documentation this is the way to handle a case when we have a variable number of FormParams passed.
This works, though.
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response test(#FormParam("param") String param) {
return Response.ok(param).build();
}
What can be the reason the multivalued map doesn't? Could it be some filtering? What is an alternative for unknown number of parameters?
UPDATE
Is is due to a particularity of Jersey + Spring.
A solution can be found in this answer.
What can be the reason the multivalued map doesn't? Could it be some filtering? What is an alternative for unknown number of parameters?
By default, it seems your JAX-RS implementation is detecting the form-input and reading/processing the body before it gets to your method. Have you tried:
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response test(Form workForm) {
MultivaluedMap<String,String> work = workForm.asMap();
return Response.ok(work.keySet().size()).build();
}
?
Form is a special JAX-RS class that encapsulates all the form parameters and should be usable as an input parameter to your method.

POST doesn't work Spring java

I have an web application and I'm trying to creat a simple POSt method that will have a value inside the body request:
#RequestMapping(value = "/cachettl", method = RequestMethod.POST)
#CrossOrigin(origins = "http://localhost:3000")
public #ResponseBody String updateTtl(#RequestBody long ttl) {
/////Code
}
My request which I call from some rest client is:
POST
http://localhost:8080/cachettl
Body:
{
"ttl": 5
}
In the response I get 403 error "THE TYPE OF THE RESPONSE BODY IS UNKNOWN
The server did not provide the mandatory "Content-type" header."
Why is that happening? I mention that other GET requests are working perfectly.
Thanks!
Edit:
When I tried it with postman the error message I got is "Invalid CORS request".
Spring application just doesn't know how to parse your message's body.
You should provide "header" for your POST request to tell Spring how to parse it.
"Content-type: application/json" in your case.
You can read more about http methods here: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_and_retrieving_form_data
Updated:
Just in case of debug, remove useless annotations to test only POST mechanism. Also, change types of arg and return type. And try to use case-sensitive header.
#RequestMapping(value = "/cachettl", method = RequestMethod.POST)
public void updateTtl(#RequestBody String ttl) {
System.out.println("i'm working");
}
Since the error is about the response type, you should consider adding a produces attribute, i.e :
#RequestMapping(value = "/cachettl", method = RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
Since you are also consuming JSON, adding a consumes attribute won't hurt either :
#RequestMapping(value = "/cachettl", method = RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
The error message is slightly misleading. Your server code is not being hit due an authentication error.
Since you say spring-security is not in play then I suspect you're being bounced by a CORS violation maybe due to a request method restriction. The response body generated by this failure (if any at all) is automatic and will not be of the application/json type hence the client failure. I suspect if you hit the endpoint with something that doesn't care for CORS such as curl then it will work.
Does your browser REST client allow you to introspect the CORS preflight requests to see what it's asking for?

GetMapping to produce CSV file using Spring Boot

I'm writing a spring rest method to get details from database and set it in a response POJO and then return it. Currently I need to produce this response in CSV instead of default json when the URL is hit using POSTMAN or RC like a downloadable CSV file with data. I googled many sites but am not sure of few logics.
Do we need to write business logic to convert pojo class values to csv format or does spring has any conversion mechanism ?
Produces = "text/csv" is being mentioned on many places, does this convert response properly ?
Currently I haven't written any code for CSV conversion.
#GetMapping("/batch/export" , produces="text/csv")
public ResponseEntity<ApplicationResponse> getBatchDetails(
HttpServletRequest request) {
ApplicationRequest appRequest = ApplicationServiceMapper.mapRequestFromHttpRequest(request);
ApplicationResponse response = appService.getDBDetails(appRequest);
return new ResponseEntity<>(response, HttpStatus.OK);
}
Here response is the one that service returns with all the data in its pojo format and if we do not give produces in annotation then by default spring will return response json. Can someone guide me? Thanks in advance.
Just using the #GetMapping with produces="text/csv" is not enough. It is only responsible for setting the response header Content-Type: text/csv.
You'll need to add the response as a parameter HttpServletResponse response, convert your POJO into a valid csv file, and only then, write the csv file into the HttpServletResponse.

Content-type and #ResponseBody in spring

It could be very simple but it will be very helpful for me to understand...
I used #ResponseBody in my restcontroller to return String value to browser. The response string is successfully received in browser.
ie:
#RequestMapping(value="/foo", method=RequestMethod.GET)
#ResponseBody
public String foo() {
return "bar";
}
What is the content-type of above response? If this is going to be like writing setAttribute in servlet response what could the attribute name?
If the browser accept only "application/json" how spring will treat the response?
Submitted code produces text/html, as do all mapped Controller methods by default. If you want to produce application/json, you have to change your RequestMapping to
#RequestMapping(value="/foo", method=RequestMethod.GET, produces = "application/json")
However this is not a valid Json String, you would have to change it because the method you submitted would return empty body. The submitted example would be valid text/plain.
When the request contains header "Accept: application/json" and other content type is returned, Spring returns Json-type response explaining that HttpMediaTypeNotAcceptableException was thrown.
Regarding the servlet analogy - please explain, I don't fully understand what you mean. The String is returned as response body, it's very different from request attributes. What would you like to achieve?
I assume the content type will be plain/text. If the request sets accept to "application/json" it depends on your browser/tool. Most rest clients won't display it as it is not application/json. If you invoke the API directly I would assume it is displayed due to browser content sniffing (can be disabled via a header).

Categories

Resources