Mapping incoming JSON to a class in spring boot - java

I'm struggling to understand why I'm getting the following error when I call my spring boot end point
{
"timestamp": 1489573322678,
"status": 406,
"error": "Not Acceptable",
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Could not find acceptable representation",
"path": "/quotes"
}
This is the request that I'm sending to the server
POST /quotes HTTP/1.1
Host: localhost:8080
tamid: 5
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 94370a3f-6165-106f-f27f-44a44093e0d5
{
"test": "works"
}
I would like the incoming JSON request body to map to a java class I have defined. Here is the class.
#Embedded
public class QuoteVersion {
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public void validate() {
}
}
I'm using the #Embedded annotation for use with a mongodb mapping library that I'm hoping is unrelated to the issue I'm facing
Here is the controller method
#RequestMapping(
path = "/quotes",
method = RequestMethod.POST,
headers = "Accept=application/json",
produces = "application/json"
)
public #ResponseBody QuoteStatus create (#RequestHeader(value = "tamid") String tamId,
#RequestBody QuoteVersion firstQuoteVersion) {
// final QuoteVersion firstQuoteVersion = this.quoteFactory.createQuoteVersion(incomingQuote);
final User currentUser = User.getFromTamId(tamId);
currentUser.can(Permissions.CREATE_QUOTE);
firstQuoteVersion.validate();
final Quote newQuote = new Quote();
newQuote.addVersion(firstQuoteVersion);
this.dataRepository.save(newQuote);
QuoteStatus qs = new QuoteStatus(newQuote);
return qs;
}
I'm guessing that Spring Boot for some reason does not understand how to map the incoming payload to the class I have defined but I have no idea how to fix the issue. Thanks in advance for any help you may have to offer.

Spring clearly indicates this problem:
HttpMediaTypeNotAcceptableException
This means that in your content-type header you provided the wrong information or made a syntactical mistake. Try putting there something like application/json.
Also
Make sure the other end will accept it. You currently only accepting requests with an accept header with value application/json. I don't think that is what you want.
So either remove that requirement or add this header to the request.

Related

Spring API REST Controller returning HTTP 406 instead of HTTP 201 after POST method

I am building and testing a simple Spring Boot API REST tutorial.
I have faced with an issue that I am trying to understand. I am getting an HTTP 406 (NOT ACCEPTABLE) when calling POST method to create and persist a new entity.
Problem is the entity is persisted but the response to the client is not what should be expected (HTTP 201 CREATED with URI in this case).
Tutorial and TutorialDto classes have the exact same attributes. Here is the definition:
public class TutorialDto {
private long id;
private String title;
private String description;
private boolean published;
...
}
Here is my POST method in #RestController class:
#PostMapping("/tutorial")
public ResponseEntity.BodyBuilder createTutorial(#RequestBody final TutorialDto tutorialDto) {
final TutorialDto createdTutorial = tutorialService.add(tutorialDto);
return ResponseEntity.created(URI.create(String.format("tutorial/%d", createdTutorial.getId())));
}
And here is the #Service method to create the entity:
#Transactional
public TutorialDto add(final TutorialDto tutorialDto) {
final Tutorial createdTutorial = tutorialRepository.save(modelmapper.map(tutorialDto, Tutorial.class));
return Optional.of(modelmapper.map(createdTutorial, TutorialDto.class))
.orElseThrow(() -> new TutorialCreationException(
String.format("Tutorial: %s could not be created", tutorialDto.getTitle()))
);
}
This is the request body:
{
"title": "tutorial",
"description": "This is the first created tutorial"
}
And this is the response body:
{
"timestamp": "2022-04-16T00:40:36.626+00:00",
"status": 406,
"error": "Not Acceptable",
"path": "/api/v1/tutorial"
}
I am getting the HTTP 406 response at the end of the controller method, after returning the "ResponseEntity.created".
Thanks in advance.
Looks like you are using wrong usage of ResponseEntity.BodyBuilder. Here is an example
Hence, your controller code should look something like this:
#PostMapping("/tutorial")
public ResponseEntity createTutorial(#RequestBody final TutorialDto tutorialDto) {
final TutorialDto createdTutorial = tutorialService.add(tutorialDto);
return ResponseEntity.created(URI.create(String.format("tutorial/%d", createdTutorial.getId()))).body(createdTutorial);
}

My microservice is not receiving "Authorization" from request header when i use feign-reactive to call it from another microservice

I have 2 microservices, ProductStore and InvoiceStore.
I want ProductStore to provide product information through an API and InvoiceStore to call that API to get product information from ProductStore.
But ProductStore needs Authorization information to check user authentication so I use #RequestHeader("Authorization") String auth as argument variable in my FeignAPI to send it to ProductStore.
But it reported that he did not receive the Authorization data when i test it.
I use #RequestHeader like that because I see it in the examples of feign-reactive all feature
I don't know if I did something wrong somewhere or I misunderstood the usage of #RequestHeader.
Help me please! Here is my code.
My ProductStore provides API to be able to get product information.
#GetMapping("products")
public ResponseEntity<String> test(#RequestHeader("Authorization") String authorization) {
log.debug("Authorization is {}", authorization);
return ResponseEntity.ok().body("all products");
}
And my InvoiceStore call that API with feign-reactive WebReactiveFeign.
I followed the instructions in the readme of Playtika feign-reactive and applied it to my project as follows
First, I write FeignAPI
#Headers({ "Accept: application/json" })
public interface FeignClientAPI {
#RequestLine("GET /products")
Mono<String> getProducts(#RequestHeader("Authorization") String authorization);
}
And then, I build the client in IvoiceService
#Service
#Transactional
public class InvoiceService {
private final FeignClientAPI client = WebReactiveFeign.<FeignClientAPI>builder().target(FeignClientAPI.class, "http://localhost:8082");
public Mono<String> testFeign(String authorization){
log.debug("Call api with authorization: {}", authorization);
return client.getTest(authorization);
}
}
And then, I create an API
#GetMapping("/invoice/test")
public Mono<ResponseEntity<String>> getProducts(#RequestHeader("Authorization") String authorization) {
return invoiceService.testFeign(authorization)
.switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND)))
.map(response -> ResponseEntity.ok().body(response));
}
Finally, I shoot an GET request to localhost:8083/invoice/test and I got an error
{
"title": "Internal Server Error",
"status": 500,
"detail": "[400 Bad Request] during [GET] to [http://localhost:8082/products] [FeignClientAPI#getTest(String)]: [{\n \"title\" : \"Bad Request\",\n \"status\" : 400,\n \"detail\" : \"Required request header 'Authorization' for method parameter type String is not present\",\n \"path\" : \"/products\",\n \"message\" : \"error.http.400\"\n}]",
"path": "/invoice/test",
"message": "error.http.500"
}
Tell me where i did wrong, Please!!!
Thank you for everything.
your code is totally wrong and i think you should get compile time error because testFeign(String authorization) need a string input but when you call it ( invoiceService.testFeign().switchIfEmpty ... ) you are not passing any input to it.
i should check the main code ,but i think you are passing null value as authorization in client side ( probably ).
I found the solution to this problem.
I misinterpreted how to use reactive feign before, which resulted in it not working.
I've added #EnableReactiveFeignClients and #EnableFeignClients for my spring boot app
#EnableReactiveFeignClients
#EnableFeignClients
public class AnswerStoreApp {
// main method
}
and then, I create an interface with #ReactiveFeignClient(name = "my-other-service")
#ReactiveFeignClient(name = "my-other-service")
public interface FeignClientService {
#GetMapping("/api/questions/test-feign")
Mono<String> demo(#RequestHeader("Authorization") String authorize);
// More request
}
finally, I can use FeignClientService to get the data that I need
#Autowired
private FeignClientService feignClientService;
// Some method
#GetMapping("/invoice/test")
public Mono<ResponseEntity<String>> getProducts(#RequestHeader("Authorization") String authorization) {
return feignClientService.testFeign(authorization)
.switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND)))
.map(response -> ResponseEntity.ok().body(response));
}

Unable to subscribe web-hook for SharePoint online

We are unable to subscribe web-hook for SharePoint online from our Spring-Boot application.
Providing valid notification URL(https enabled, publicly accessible, valid domain name, Post method) as parameter while consuming rest API in order to subscribe web-hook.
#PostMapping(value = "/spnotification")
#ResponseBody
public ResponseEntity<String> handleSPValidation(#RequestParam final String validationtoken) {
LOG.info("validationToken : " + validationtoken);
return ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN)
.body(validationtoken);
}
And on this notification URL end-point, we are able to receive validation string from share-point as parameter and same string we are retiring in less then 5 sec with content-type text/plain and http status code 200 as response.
still getting 400 bad request with below error message.
400 Bad Request: [{"error":{"code":"-1, System.InvalidOperationException","message":{"lang":"en-US","value":"Failed to validate the notification URL 'https://example.com/notification-listener-service/api/webhook/spnotification'."}}}]
Note : We are following this API documentation to subscribe web-hook.
We tried Graph API also for the same purpose but in that case getting below error.
"error": {
"code": "InvalidRequest",
"message": "The server committed a protocol violation. Section=ResponseHeader Detail=CR must be followed by LF"
}
Please find this diagram for more understanding on this issue.
We really appreciate if someone can help us on the same.
Please check the #PostMapping(value = "/notification", headers = { "content-type=text/plain" })
#PostMapping(value = "/notification", headers = { "content-type=text/plain" })
#ResponseBody
public ResponseEntity<String> handleSPValidation(#RequestParam final String validationtoken) {
LOG.info("validationToken : " + validationtoken);
return ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN)
.body(validationtoken);
}
GitHub Code

Webhook to collect a JSON: Request method POST not supported

I'm writing a controller with Java for a webhook URL that receives a POST method call, with a JSON body, that I need to collect.
This is a simplified version of the controller, with Spring annotations:
#RestController
public class MyWebhoook {
#PostMapping("/my-webhook")
public void getMyJson(#RequestBody Map<String, Object> json) {
System.out.println("WebHook collected JSON: " + json);
}
}
I test it with Postman sending this JSON:
Header: Content-Type / application/json
{
"webhookKey" : "tranviaVermellCostaAvall",
"token" : "xx",
"channelId": 1,
"propertyId": "999999",
"status": "new",
"reservationId": "111211221",
"reservationStatus" : 1
}
And I get this answer:
{
"timestamp": "2019-04-09T07:23:38.093+0000",
"status": 405,
"error": "Method Not Allowed",
"message": "Request method 'POST' not supported",
"path": "/my-webhook"
}
The server log, gives some more information:
Request method 'POST' not supported, path=/my-webhook}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#7756c3cd
I've tried those different approaches, with the same result:
#RequestMapping(value = "/my-webhook", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST) instead of the #PostMapping("/my-webhook") annotation.
Modelling the received JSON as a java object instead of the Map<String, Object> json.
Using HttpServletRequest instead of the #RequestBody annotation, trying to read the request as a String.
I do not understand the reason why the POST method is not allowed. Any help, would be very much appreciated.
I added {} between your method and got a 200 as return. I added the pictures below on what code, request and console output i got.
I copied your code to my spring boot app, worked perfectly via postman...
API:
#PostMapping("/my-webhook")
public void getMyJson(#RequestBody Map<String, Object> json) {
System.out.println("WebHook collected JSON: " + json);
}
RequestBody:
{
"webhookKey" : "tranviaVermellCostaAvall",
"token" : "xx",
"channelId": 1,
"propertyId": "999999",
"status": "new",
"reservationId": "111211221",
"reservationStatus" : 1
}
URL: http://localhost:8080/my-webhook
Try to:
Update and Clean your project.
Invalidate IDE cache and restart it, and try again.
The problem was with the CSRF (Cross-site request forgery) security configuration. The path of the webhook, has to be taken out of the CSRF control. Otherwise, the POST request doesn't pass the CSRF control.
This is a simplified extract of the security settings:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http)throws Exception{
http
.csrf()
.ignoringAntMatchers("/my/webhook")

Does #RequestBody annotation works with the class having private constructor. I'm using Springboot application

I am using #RequestBody annotation in my controller on a class which is from maven dependency library and it has private constructor.
#RequestMapping(value = "/myApi", method = RequestMethod.POST, produces = "application/json; charset=utf-8")
#ApiOperation(value = "My API", response = CustomResponse.class, notes = "API response beautified")
public String apiOperation( #RequestBody #ApiParam(value = "ReqBody",required = true)
MyAPIBody apiReqBody ) {
// some code
// MyAPIBody is imported from maven dependency lib
// and has the all constructors as private
}
I am getting 415 error.
{
"timestamp": "2019-02-03T19:26:30.738+0000",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported",
}
Can someone suggest what I am doing incorrectly, or i require some change in my project structure .
I feel that it(class with private constructor) should not work with requestbody, because when I applied the RequestBody param to some other class, it worked. But, if someone could explain the reason, i could make well thought changes in my project structure. Or correct me if my feeling is incorrect.

Categories