Spring webhook endpoint get entire body as well as pojo - java

I am making an application using spring, and I have a webhook endpoint as shown below
#PostMapping("/paypal")
fun paypalMapping(
#RequestHeader headers: Map<String, String>,
#RequestBody paypalOrder: PaypalOrder
) {
// Stuff in here
}
Now, the problem I have with this is the paypal Event.validateReceivedEvent requires the entire webhook body as a string, and as you can see above, I map it to a POJO I made, meaning I do not have the entire webhook body as a string. I tried simply adding another #RequestBody body:String parameter to the method, and that just came with an error about not being able to find the body.
Thanks

You can't bind the request body twice unfortunately, you can only use the #RequestBody once. What I'd do is to bind it as a String and deserialize manually:
#PostMapping("/paypal")
fun paypalMapping(
#RequestHeader headers: Map<String, String>,
#RequestBody body: String
) {
// Stuff in here
}

Related

Spring MVC enforce request empty body

I have the following controller method for deleting some data on the server side, but I can send any value as request body which is simply ignored. Is it possible to enforce request body to be empty?
#DeleteMapping
ResponseEntity<?> delete(#RequestAttribute(USER_KEY) User user) {
service.delete(user.getId());
return ResponseEntity.ok().build();
}
there is trick explains here Spring Boot: Return a empty JSON instead of empty body when returned object is null
create EmptyJsonBody and application.properties add spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES=true
it will not allow any other json fields if you use EmptyJsonbody
ResponseEntity<?> delete(#RequestAttribute(USER_KEY) User user, #RequestBody EmptyJsonBody emptyBody) {

GetMapping with JSON array parameter

I am using a FeignClient and a GetMapping to interact with an external system.
My GetMapping is defined as:
#GetMapping(value = "/path/to/endpoint?fields=[\"nm\",\"label\"]")
public String hitEndpoint() {}
Debug shows that the endpoint is being called with:
https://url/path/to/endpoint?fields=[%22nm%22&fields=%22label%22]
Which is failing because the endpoint expects valid json for the fields parameter:
https://url/path/to/endpoint?fields=[%22nm%22,%22label%22]
How do I convince GetMapping to make the request correctly?
Thanks for any help.
Although I think its better to pass JSON as body of a POST method to your controller. But if you insist on doing this I can propose to you 2 solutions:
First Solution
Encode your JSON array into Percent-encoding so you can send it via URL.
For example, your array will be like this:
["nm","label"] -> %5B%22nm%22%2C%22label%22%5D
I used this online tool to encode it.
Second Solution
Encode your array into Base64 and GET it via URL to your controller.
Decode the given Base64 string in the controller and parse it as a JSON array.
There is no need to define your Array parameter in the URL, using spring you can define your API as below and with the power of #RequestParam annotation you can define that you expect to receive an array as a parameter in the URL
#RequestMapping(value = "/path-to-endpoint", method = RequestMethod.GET)
public ResponseEntity<YourResponseDTO> yourMethodName(
#RequestParam("ids") List<Long> arrayParam) {
// Return successful response
return new ResponseEntity<>(service.yourServiceMethod(requestDTO), HttpStatus.OK);
}
Then you can call your GET endpoint using below URL:
/path-to-endpoint?ids=1,2,3,4

Adding another MediaType to REST api endpoint that is used by other clients

In my app I am sharing REST api using Spring MVC, which users may use in their custom apps. Let's say I have an endpoint in Controller class:
#GET
#Path("/getNumber")
#Produces({ MediaType.TEXT_PLAIN })
public String getNumber(Long id) {
return service.getNumber(id);
}
which response is available only as TEXT_PLAIN. What would happen, if for example one of the client say that his app will not work when he gets response as plain text and that the endpoint should return json/or should have possibility to return response in json? So when I add another MediaType to annotation #Produces, may that cause problems with other users custom apps using this endpoint? Because for ex. in their apps, client may be expecting response as plain text, and by getting response as json, response will not be handled correctly?
If adding this MediaType may cause problems, what can I do to take into account users custom apps using this endpoint? Should I create similar endpoint, but this one will have MediaType APPLICATION_JSON, or both, with added for ex. "/v2" in endpoint path, or is there some better solution for that?
#GET
#Path("/v2/getNumber")
#Produces({ MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON })
public String getNumber(Long id) {
return service.getNumber(id);
}
No need to create multiple endpoints. You can define multiple mediaTypes in #Produces.
Inorder to decide which MediaType should be sent as response, Client has to set the type of MediaType they are expecting in the "Accept" header when making the request.
Based on this "Accept" header you can decide what content-type to return.
you can use "consume" keyword for Multiple media type
#GET(value = "/configuration", consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE
})
public String setConfiguration(#RequestPart MultipartFile file,
#RequestBody Configuration configuration)

Feign Client Call(to a http service) is converted from GET request to POST when the call is made in runtime

I was trying to call a http GET service through feign client but I see the request being converted into POST before the call is made.
Without header the Get call works fine(I meant gives a 401 instead 404), the header content is expected by http service I'm trying to call, so cannot remove the header.
#FeignClient(name="commonservice")
#Path("/company/service/module")
public interface getCaseInfo{
#GET
#Path("/endpointURI/{pathparam}")
public ResponseObject getCaseDetails(#PathParam("pathparam") String param, #RequestHeader Map<String,String> header) throws exception
}
/TRIED #HeaderParam Annotation as well/
The feign client call has to be made as GET request with Headers
I was able to resolve the issue by using #headerparam for each of the custom header parameter I had. But it would still keep doing that GET to POST, if i use:
"#Headerparam/#HeaderMap Map headers"
Updated code:
public ResponseObject getCaseDetails(#PathParam("pathparam") String param, #HeaderParam("custom_header1") String custom_header1,#HeaderParam("custom_header2") String custom_header2)
If someone can give me the reason on why a map wouldn't work, that would be great!

How to get JSON String with Subdomain in Java Map at Spring MVC Controller

I'm trying to receive a JSON String with subdomain to a Java Map at Spring MVC Controller.
Let's say I have this JSON at the JavaScript part:
var contract = {dateFrom:someDate,
dateTo:someDate,
season: {seasonCode:someString}}
The JSON is sent as GET Ajax call.
The Controller looks like this:
#RequestMapping(value = "/", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<String> getVertagFromSearch(#RequestParam Map<String, Object> allRequestParams, ModelMap model){
The output of the Map looks like this:
{dateFrom=02-07-2014, dateTo=02-07-2014, season[seasonCode]=SO03}
The GET Request looks like this:
http://localhost:8080/contracts/?dateFrom=02-07-2014&dateTo=02-07-2014&season%5BseasonCode%5D=SO03
I want to parse this Map into my contract domain object. But with this structure it doesnt work. Without a subdomain(season) it worked.
Thanks in advance!
Update 1
Sending as an object it looks like this: (output Browser)
Object { dateFrom: "09-07-2014", dateTo: "08-07-2014", season: Object }
Sending after JSON.stringify it looks like this:
"{"fromDate":"09-07-2014","dateTo":"08-07-2014","season":{"saiCode":"SO03"}}"
In this case I think the probem are the double quotes at the beginning and at the end.
I think the best two options are:
1) Modify your JS object to have only simple objects. If you look at your request URL before your update, you had:
{your IP:port}/contracts/?dateFrom=02-07-2014&dateTo=02-07-2014&season%5BseasonCode%5D=SO03
That is not JSON at all, is a simple request with three parameters:
dateFrom = 02-07-2014
dateTo = 02-07-2014
season%5BseasonCode%5D= SO03 // %5B and %5D are '[' and ']' escaped
So your javascript is transforming a JS object (not JSON) in plain parameters.
2) Send a parameter, a string, with your JSON structure and then use some JSON library to parse it:
http://localhost:8080/contracts/?myJson=<JSON_String>
and then modify your controller as:
#RequestMapping(value="/", method=RequestMethod.GET, headers="Accept=text/plain")
public ResponseEntity<String> getVertagFromSearch(#RequestParam String myJson, ModelMap model){
JSONObject myJSON= new JSONObject (myJson);
...
}
Usually sending a JSON is easier with a POST (just adding it to the body), but as your request seems to ask for data, a POST request is not a good idea. Most of the rest APIs use the first approach, JSON tends to be part of a response more than part of a GET request.

Categories