Spring MVC Override Received Content Type - java

I'm working on a Spring MVC application and have a client that I have no control over. This client is POSTing JSON data but transmitting a application/x-www-form-urlencoded header. Spring naturally trusts this header and tries to receive the data but can't because its JSON. Has anyone had experience overriding the header that Spring receives or just specifying exactly what type of data is coming, regardless of the headers?

You can do two things;
Change the client to send the Content-Type:
application/json header
Write a Servlet Filter or Spring Interceptor which is on top of the Spring Controller and checks for the header Content-Type. If it is not application/json then it changes it to application/json.

Why don't you write a separate controller to handle application/x-www-form-urlencoded requests. If the request is a valid JSON, then you can parse it and forward it to to appropriate service.
This way you can also handle a case in future where you get request of same type which is not a valid JSON.
#RequestMapping(value = "/handleURLEncoded", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public #ResponseBody Object handleURLEncoded(HttpEntity<String> httpEntity) {
String json = httpEntity.getBody();
//now you have your request as a String
//you can manipulate it in any way
if(isJSONValid(json)) {
JSONObject jsonObj = new JSONObject(json);
//forward request or call service directly from here
//...
}
//other cases where it's not a valid JSON
}
Note: isJSONValid() method copied from this answer

Related

Spring Controller GET/POST/PUT to another interface

I am using React as frontend and Java Spring Boot as backend.
React sends JSON form data as GET/PUT/POST requests to my backend url (http://localhost:8080/test). Now, I wan't to send this JSON forward to another interfaces GET endpoint (https://another/interface/add?id={id}). This interface then queries database based on the id and answers 200 OK message with a JSON reply which I need to display (send back to frontend).
1. What is the correct way of sending a request to another interface from Spring Boot backend? In the same method I catched the frontends data?
2. I also have to set HTTP headers to the GET request, how would I go on about this?
Example of how Frontend is sending an id field as a JSON to backend:
React POST
addId = async (data) => {
return this.post(/localhost:8080/test/id, data)
}
Example of how Backend is receiving the id field:
Spring Boot POST
#PostMapping("test/id")
public String test(#RequestBody String id) {
return id;
}
As I understand you want to get data from backend with json body and httpstatuscode 200 . Am i right?
May be you can try this
#GetMapping(/interface/add)
public ResponseEntity<?> test(#RequestParam("id") String id){
//write code you want
return ResponseEntity.status(HttpStatus.OK).body("string" or dto possible);
}
ResponseEntity send body with httpstatus code and if you want to send requestparam you set #RequestParam annotation to set .
When I do project with springboot and react. I use json type to exchange data. And Most Services usually exchange data with json data type.
2.I confused about this Question if you send data React to springboot your code is right
Axios.get("localhost....", data) you can change http type with
Axios.(get, post, delete)

GET call with request body - request body not accessible at controller

I am trying to do a get call with request body(JSON) as the request parameter list exceeds the limit. I am able to send the request via postman/insomnia and request is reaching till controller without any error. But the "requstBody" is empty at controller. What i am missing here?
#GET
#Path("\path")
#Consumes(APPLICATION_JSON)
#Produces(APPLICATION_JSON)
public Response getResponse(String requestBody) throws IOException { }
When I replaced #GET with #POST, requestBody has value. For GET call do we need to add anything more?
I am trying to do a get call with request body(JSON) as the request parameter list exceeds the limit. I am able to send the request via postman/insomnia and request is reaching till controller without any error. But the "requstBody" is empty at controller. What i am missing here?
One thing you are missing is the fact that the semantics of a request body with GET are not well defined.
RFC 7231, Section 4.3.1:
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
There are two ways for sending parameters in an Http Get method. PathVariable and RequestParam. In this way, sent parameters are visible in the request URL. for example:
www.sampleAddress.com/countries/{parameter1}/get-time?city=someValues
In the above request, parameter1 is a path variable and parameter2 is a request parameter. So an example of a valid URL would be:
www.sampleAddress.com/countries/Germany/get-time?city=berlin
To access these parameters in a java controller, you need to define a specific name for the parameters. For example the following controller will receive this type of requests:
#GetMapping(value = "/countries/{parameter1}/get-time", produces = "application/json; charset=utf-8")
public String getTimeOfCities(
#PathVariable(value = "parameter1") String country,
#RequestParam(value = "city") String city
){
return "the method is not implemented yet";
}
You are able to send RequestBody through a Get request but it is not recommended according to this link.
yes, you can send a body with GET, and no, it is never useful
to do so.
This elaboration in elasticsearch website is nice too:
The HTTP libraries of certain languages (notably JavaScript) don’t allow GET requests to have a request body. In fact, some users are suprised that GET requests are ever allowed to have a body.
The truth is that RFC 7231—the RFC that deals with HTTP semantics and
content—does not define what should happen to a GET request with a
body! As a result, some HTTP servers allow it, and some—especially
caching proxies—don’t.
If you want to use Post method, you are able to have RequestBody too. In the case you want to send data by a post request, an appropriate controller would be like this:
#PostMapping(value = "/countries/{parameter1}/get-time", produces = "application/json; charset=utf-8")
public String getTimeOfCitiesByPost(
#PathVariable(value = "parameter1") String country,
#RequestParam(value = "city") String city,
#RequestBody Object myCustomObject
){
return "the method is not implemented yet";
}
myCustomObject could have any type of data you defined in your code. Note that in this way, you should send request body as a Json string.
put #RequestBody on String requestBody parameter
#RequestMapping("/path/{requestBody}")
public Response getResponse(#PathVariable String requestBody) throws IOException { }

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).

Dynamically passing JSON/XML to REST web services

I have created a REST web services. Now the application which calls my web services says that will send a Header as RESPONSETYPE with value as JSON or XML. Based on this, I need to produce the response in json/xml. I understand that the Accept header can be used by sending the value as application/xml or application/json. But how can I achieve the dynamic response based on the custom header RESPONSETYPE?
Thanks in advance.
You should be able to do this by setting the MediaType explicitly in your Response object.
#GET
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response getSomething(#HeaderParam("your-customer-header") String customHeaderType) {
return Response.ok(thingYouWantToReturn, mediaTypeFrom(customHeaderType)).build();
}
mediaTypeFrom is a method you'll need to determine what the actual MediaType to return is

Is there any way to configure spring mvc to assume a content-type?

I have a method to which I want to post some json data, that looks like this
#RequestMapping(value = "/m1", method = RequestMethod.POST)
public Object m1(#RequestBody Map<String, ?> body) {
// do something
}
This works great when I set the content-type header to application/json when I post, but fails with an error if I don't (it cannot deserialize the post body into the map because it doesn't know how)
What would I have to configure in spring to make it use application/json as a default when no header is specified?
The class that converts the JSON to your object is called an HttpMessageConverter. I assume you are using the default Jackson one that comes with Spring. You can write a custom MessageConverter, that will always return true in it's supports method with your response object type and then just call the Jackson httpconverter in your readInternal and writeInternal methods. If you do this however, be careful, as once it's registered in your requesthandler, it will be asked on all #ResponseBody and #RequestBody requests.

Categories

Resources