Parsing Base64 json directly into a POJO in a spring MVC controller - java

code for the controller
#RequestMapping(value = "/second", method = RequestMethod.POST,
consumes = "application/json")
#ResponseBody
public void test(#RequestBody RegistrationRequest
registrationRequest)
{
System.out.println(registrationRequest.toString());
}
POJO
#Data
#JsonDeserialize(using = Base64Deserializer.class)
public class RegistrationRequest {
#JsonProperty("payment-method-details")
public PaymentMethodDetails paymentMethodDetails;
#JsonProperty("customer-details")
public CustomerDetails customerDetails;
}
CustomerDetails, PaymentMethodDetails are POJO of their own.
I am very new to the Spring MVC concepts, and this is all I have figured out till now but while making the post request from the Postman, it isn't working. What is wrong that I am doing?
I can not maneuver anything how the request is going to come.
Using Base64 deserialiser
Decode base64 encoded JSON to POJO with jackson and spring-boot

It is not clear from the question, about the desired format of the request.
Above code should work fine with a request like this :
curl -X POST "http://myhost:port/rootPath/second/" -d '{"payment-method-details":"eyJwYXltZW50TWV0aG9kRGV0YWlsc1ZhcjEiOiJzb21lIHRleHQiLCJwYXltZW50TWV0aG9kRGV0YWlsc1ZhcjIiOiJzb21lIHRleHQyIn0=", "customer-details":"eyJjdXN0b21lckRldGFpbHNWYXIxIjoic29tZSB0ZXh0IiwiY3VzdG9tZXJEZXRhaWxzVmFyMiI6InNvbWUgdGV4dDIifQ=="}'
This will get the above code working and prints the output.
Note, here eyJjdXN0b21lckRldGFpbHNWYXIxIjoic29tZSB0ZXh0IiwiY3VzdG9tZXJEZXRhaWxzVmFyMiI6InNvbWUgdGV4dDIifQ== decodes to {"customerDetailsVar1":"some text","customerDetailsVar2":"some text2"} in base64 format, similar is the case for other variable.

Related

HTTP Status 415 – Unsupported Media Type when doing POST request on Spring MVC

I am trying to send a post request on a simple Spring MVC web app and use RequestBody in my controller to convert the JSON into a Java Object but for whatever reason, I keep getting HTTP Status 415 – Unsupported Media Type. I have spent a lot of time trying to find a solution to this but nothing seems to be working.
The get method in my Controller seems to be working fine. This is my original code
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping(value = "/users", method = RequestMethod.POST)
public class MyControllerAgain {
#RequestMapping(method = RequestMethod.POST, consumes = "application/json")
public void handleJson(#RequestBody Contact c) {
System.out.println(c);
}
#RequestMapping(method = RequestMethod.GET, consumes = "application/json")
public void handleGet() {
System.out.println("a");
}
}
This is my Contact
public class Contact {
int id;
public String name;
public int number;
public Contact(){}
// Getters and setters
}
I am sending a request with Postman and this is what it looks like
POST /users HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Accept: application/json
Cache-Control: no-cache
Postman-Token: 511c9e03-4178-380d-58f8-db24a3245b9e
{
"id":1,
"name":"something",
"number":1
}
I have also tried including Jackson dependencies in my pom.xml .
I have tried altering consume value in #RequestMapping annotation and I have tried all combinations of headers accept and Content type in my request.
Also, If I use #ModelAttribute instead of #RequestBody, then everything works fine except all the fields in Contact class are null.
Here is the github link - https://github.com/Sanil2108/test_springmvc
To me it looks like that the jpa anotations are messing up the json deserialization.
The error returned from the spring server could be misleading.
Try using plain object with setters and getters and see if this changes anything.
You should search for some exceptions in the logs.
RequestMapping annotation not only has the consumes but also produces.
But to avoid all these settings for HTTP REST, you can use RestController annotation and GetMapping, PostMapping etc..
You can find an example in my github
Add a mapping to the handleGet method, for example:
#RequestMapping(value = "/get", method = RequestMethod.GET, consumes = "application/json")
public void handleGet() {
System.out.println("a");
}
--UPDATE--
Remove the consumes = "application/json" part from you GET call. It sees that both requests that listen to "/users" can consume json data, but one is a GET and the other one is a POST.
--2nd UPDATE--
This will definitely work. Tested.
#RestController
#RequestMapping(value = "/users", method = RequestMethod.POST)
public class ContactController
{
#RequestMapping(method = RequestMethod.POST, consumes = "application/json")
public void handleJson(#RequestBody Contact c)
{
System.out.println(c);
}
}
Tried everything but couldn't it to work. Maybe I was making a silly mistake somewhere or there was something seriously wrong with my configuration. Anyway, I tried to make it work with Spring boot and it worked fine. For anyone who is interested, Here is the github link - https://github.com/Sanil2108/spring_hibernate/tree/master/spring_boot1
Also, thanks to everyone who tried to help!

How to call a #RestController with #RequestBody?

I have a simple servlet as follows:
#RestController
public class TestServlet {
#RequestMapping(value = "/test1")
public String test1() {
return "test1";
}
#RequestMapping(value = "/test2")
public String test2(#RequestBody TestClass req) {
return "test2";
}
public static class TestClass {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
But only the servlet not receiving parameters is working:
Works: http://localhost:8080/test1
Doesn't work: http://localhost:8080/test2?value=1234
org.springframework.http.converter.HttpMessageNotReadableException:
Required request body is missing: public java.lang.String
Why is the #RequestBody annotation not working? Am I missing an important piece?
One of the differences between #Controller and #RestController is that you don't have to write #RequestBody and #ResponseBody, that means that any parameter in your controller method which does not have an annotation (like #PathVariable, #ModelAttribute, ...) will implicitly have #RequestBody, and must therefore be POSTed as the HTTP entity body. So you need to send JSON/XML as part of a POST. What you have done is to send data on as part of the URL, which makes it a request parameter and not body-data, and you need #RequestParam to to extract data from the URL.
Also, I would recommend that you use the #GetMapping/#PostMapping or include the method parameter in the #RequestMapping annotation, it is highly unlikely that you want a service to be used for both POST and GET, so you should be as specific as possible in you controller method descriptions, to limit error scenarios.
The reason the second URL does not work is because when using #RequestBody the data you are sending to the endpoint needs to come through via the data attribute in the request header. When you append ?attr=value to your URL that is sending the attribute in the params header.
There are two ways to fix this:
Change your endpoint to read something like this:
public String test2(#RequestParam("value") TestClass req) {
//Endpoint code
}
Change your endpoint to read something like this:
#RequestMapping(value="test2",method=RequestMethod.POST)
public String test2(#RequestBody TestClass req){
//Endpoint code
}
and make your call similar to this (e.g. angularjs):
http.post({url:/*url*/,data:/*object to send*/});
The second option will most likely be what you want to go with because it looks like you are trying to send a json object to your endpoint and I believe you can only do that by making a POST request rather than a GET request
Just leave out the #RequestBody annotation, as this is only for POST requests.
public String test2(#Valid TestClass req) {
return "test2";
}
When you declare a controller method parameter as #RequestBody, you are wishing it to be recovered from the request body and not as a "regular" http parameter.
You could try using any kind of plugin for Firefox (RESTClient) or Chrome (PostMan) and try using one of them. You could do it using SoapUI as well.
The request should be a POST to the requested url this way:
POST http://localhost:8080/test2
You must provide http headers provinding expected Content-Type and Accept. In case of using Json, set them like this:
Content-Type: application/json
Accept: text/html (As your method returns only a String)
And then write the param to the request body. If in Json, like this:
{
"value":"the provided value"
}

Testing Spring MVC POST with IntelliJ REST client causes 415

I'm developing an application in Java/Spring MVC and have no problem with testing my GET methods. The problem occur then I try to test the POST using #RequestBody.
The error:
HTTP 415 The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
I created a simple test to show my problem:
#RestController
#RequestMapping("/test")
public class ConcreteTestController implements TestController {
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.OK)
#Override
public void add(#RequestBody Dummy dummy) {
System.out.println(dummy);
}
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
#Override
public Dummy get() {
Dummy dummy = new Dummy();
dummy.setName("apa");
return dummy;
}
}
The Dummy class is very simple:
public class Dummy {
private String name;
public Dummy() {}
// Omitted setters and getters.
}
The jsonresponse from the GET looks like this:
{"name":"apa"}
I'm starting the IntelliJ REST client and using the json above as request body. I've tried using both application/json and / under Accept in the header with no difference in result.
Any idea what could cause this? I'm stuck and would appreciate help.
By default you have to add Content-Type manually in the REST client in IntelliJ. I had forgotten to do so and to set it to application/json. After having done so it is working fine.

Spring boot Rest controller - convert form-encoded body to POJO

I would like to process an HTTP POST request hitting my rest endpoint and convert its body to my defined POJO. I was successful in the past with mapping between JSON and POJOs but I am struggling with this form encoded content.
I have a controller as follows:
#Slf4j
#RestController("/example")
public class GatewayController {
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void describeInstances(#RequestBody MyPojo body) {
log.debug("Value1: " + body.getValue1());
}
}
And a POJO:
#Data
public class MyPojo {
private String value1;
private String value2;
...
}
I am hitting the controller with a POST request which looks as follows:
Content-Type: application/x-www-form-urlencoded
Body: value1=abc&value2=efg...
But all I am getting is 415 Unsupported Media Type. Any help is appreciated.
EDIT: when I change the POJO into String and just print the body, it works (without receving the unsupported media type exception) so it is definitely in the POJO declaration.
Follow up:
Is it possible to annotate the POJO attributes to allow different names of the fields (to avoid using undorscope inside variable names, for example)?
Something like:
#Attribute("value_name")
private String valName;
#ModelAttribute is the recommended way to get form data in your controller. e.g.
public void describeInstances(#ModelAttribute("mypojo") MyPojo body)
and
#Data
#ModelAttribute("mypojo")
public class MyPojo {
Read the spring docs for more info here

Spring MVC - How to return simple String as JSON in Rest Controller

My question is essentially a follow-up to this question.
#RestController
public class TestController
{
#RequestMapping("/getString")
public String getString()
{
return "Hello World";
}
}
In the above, Spring would add "Hello World" into the response body. How can I return a String as a JSON response? I understand that I could add quotes, but that feels more like a hack.
Please provide any examples to help explain this concept.
Note: I don't want this written straight to the HTTP Response body, I want to return the String in JSON format (I'm using my Controller
with RestyGWT which requires the response to be in valid JSON
format).
Either return text/plain (as in Return only string message from Spring MVC 3 Controller) OR wrap your String is some object
public class StringResponse {
private String response;
public StringResponse(String s) {
this.response = s;
}
// get/set omitted...
}
Set your response type to MediaType.APPLICATION_JSON_VALUE (= "application/json")
#RequestMapping(value = "/getString", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
and you'll have a JSON that looks like
{ "response" : "your string value" }
JSON is essentially a String in PHP or JAVA context. That means string which is valid JSON can be returned in response. Following should work.
#RequestMapping(value="/user/addUser", method=RequestMethod.POST)
#ResponseBody
public String addUser(#ModelAttribute("user") User user) {
if (user != null) {
logger.info("Inside addIssuer, adding: " + user.toString());
} else {
logger.info("Inside addIssuer...");
}
users.put(user.getUsername(), user);
return "{\"success\":1}";
}
This is okay for simple string response. But for complex JSON response you should use wrapper class as described by Shaun.
In one project we addressed this using JSONObject (maven dependency info). We chose this because we preferred returning a simple String rather than a wrapper object. An internal helper class could easily be used instead if you don't want to add a new dependency.
Example Usage:
#RestController
public class TestController
{
#RequestMapping("/getString")
public String getString()
{
return JSONObject.quote("Hello World");
}
}
You can easily return JSON with String in property response as following
#RestController
public class TestController {
#RequestMapping(value = "/getString", produces = MediaType.APPLICATION_JSON_VALUE)
public Map getString() {
return Collections.singletonMap("response", "Hello World");
}
}
Simply unregister the default StringHttpMessageConverter instance:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
/**
* Unregister the default {#link StringHttpMessageConverter} as we want Strings
* to be handled by the JSON converter.
*
* #param converters List of already configured converters
* #see WebMvcConfigurationSupport#addDefaultHttpMessageConverters(List)
*/
#Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(c -> c instanceof StringHttpMessageConverter);
}
}
Tested with both controller action handler methods and controller exception handlers:
#RequestMapping("/foo")
public String produceFoo() {
return "foo";
}
#ExceptionHandler(FooApiException.class)
public String fooException(HttpServletRequest request, Throwable e) {
return e.getMessage();
}
Final notes:
extendMessageConverters is available since Spring 4.1.3, if are running on a previous version you can implement the same technique using configureMessageConverters, it just takes a little bit more work.
This was one approach of many other possible approaches, if your application only ever returns JSON and no other content types, you are better off skipping the default converters and adding a single jackson converter. Another approach is to add the default converters but in different order so that the jackson converter is prior to the string one. This should allow controller action methods to dictate how they want String to be converted depending on the media type of the response.
I know that this question is old but i would like to contribute too:
The main difference between others responses is the hashmap return.
#GetMapping("...")
#ResponseBody
public Map<String, Object> endPointExample(...) {
Map<String, Object> rtn = new LinkedHashMap<>();
rtn.put("pic", image);
rtn.put("potato", "King Potato");
return rtn;
}
This will return:
{"pic":"a17fefab83517fb...beb8ac5a2ae8f0449","potato":"King Potato"}
Make simple:
#GetMapping("/health")
public ResponseEntity<String> healthCheck() {
LOG.info("REST request health check");
return new ResponseEntity<>("{\"status\" : \"UP\"}", HttpStatus.OK);
}
Add produces = "application/json" in #RequestMapping annotation like:
#RequestMapping(value = "api/login", method = RequestMethod.GET, produces = "application/json")
Hint: As a return value, i recommend to use ResponseEntity<List<T>> type. Because the produced data in JSON body need to be an array or an object according to its specifications, rather than a single simple string. It may causes problems sometimes (e.g. Observables in Angular2).
Difference:
returned String as json: "example"
returned List<String> as json: ["example"]
Add #ResponseBody annotation, which will write return data in output stream.
This issue has driven me mad: Spring is such a potent tool and yet, such a simple thing as writing an output String as JSON seems impossible without ugly hacks.
My solution (in Kotlin) that I find the least intrusive and most transparent is to use a controller advice and check whether the request went to a particular set of endpoints (REST API typically since we most often want to return ALL answers from here as JSON and not make specializations in the frontend based on whether the returned data is a plain string ("Don't do JSON deserialization!") or something else ("Do JSON deserialization!")). The positive aspect of this is that the controller remains the same and without hacks.
The supports method makes sure that all requests that were handled by the StringHttpMessageConverter(e.g. the converter that handles the output of all controllers that return plain strings) are processed and in the beforeBodyWrite method, we control in which cases we want to interrupt and convert the output to JSON (and modify headers accordingly).
#ControllerAdvice
class StringToJsonAdvice(val ob: ObjectMapper) : ResponseBodyAdvice<Any?> {
override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean =
converterType === StringHttpMessageConverter::class.java
override fun beforeBodyWrite(
body: Any?,
returnType: MethodParameter,
selectedContentType: MediaType,
selectedConverterType: Class<out HttpMessageConverter<*>>,
request: ServerHttpRequest,
response: ServerHttpResponse
): Any? {
return if (request.uri.path.contains("api")) {
response.getHeaders().contentType = MediaType.APPLICATION_JSON
ob.writeValueAsString(body)
} else body
}
}
I hope in the future that we will get a simple annotation in which we can override which HttpMessageConverter should be used for the output.
Simple and Straightforward send any object or return simple List
#GetMapping("/response2")
#ResponseStatus(HttpStatus.CONFLICT)
#ResponseBody List<String> Response2() {
List<String> response = new ArrayList<>(Arrays.asList("Response2"));
return response;
}
I have added HttpStatus.CONFLICT as Random response to show how to pass RequestBody also the HttpStatus
Annotate your method with the #ResponseBody annotation to tell spring you are not trying to render a view and simple return the string plain

Categories

Resources