Transferring an object via REST - java

I've been trying to send an object from one application to another using rest.
Sender:
#Controller
public class Sender {
#RequestMapping(value = "/comMessageApp-api/getMessages")
public String restGetMessages() {
String url = "http://localhost:8079/comMessageApp-api/responseMessages";
HttpEntity<Dto2> entity = new HttpEntity<>(new Dto2());
ResponseEntity<Dto2> response = restTemplate.exchange(url, HttpMethod.POST, entity, Dto2.class);
}
}
Receiver:
#RestController
public class Receiver {
#RequestMapping(value = "/comMessageApp-api/responseMessages")
public void restResponseMessages(HttpEntity<Dto2> request) {
System.out.println(request.getBody());
}
}
DTO:
public class Dto2 {
private String string = "Test string";
public Dto2() {
}
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
Jackson is used serialization/deserialization.
Any ideas, why request.getBody() printed in the Receiver is null???
I tried to send the object inside HttpEntity and inside RequestEntity. No success in both cases. On the receiving side I always get null.

Your sender (client) side is very close but your server side doesn't return a value so change the type to Void:
ResponseEntity<Void> response = restOps.exchange(url, HttpMethod.POST, entity, Void.class);
Your receiver (server) side is not quite set up correctly either, you need to set the HTTP method to [edited] POST. You'll also need to tell Spring to map the body of the request (your rest payload) onto the parameter;
#RequestMapping(value = "/comMessageApp-api/responseMessages", method=RequestMethod.POST)
public void recieveDto (#RequestBody final Dto dto) {
System.out.println(dto.toString());
}
[EDIT] Brainfart, the http method should be set to POST on receive annotation.
[Further suggestion]
403 errors may be due to Spring Security, if you have it switched on (check your POM if you're not sure) try this;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests().
antMatchers("/**").permitAll();
}
}
You'll want to be tightening up security once you know it works.

try to use #RequestMapping(method = RequestMethod.POST, produces = "application/json", consumes = "application/json")

Related

PageNotFound - Request method 'GET' not supported

I am getting this error while calling an API from postman, after I hosted my spring app in VM. Locally it works. But Get methods in my VMs are working.
[http-nio-8081-exec-4] PageNotFound - Request method 'GET' not supported
My controller method looks like this:
#RestController
#RequestMapping("/orders/")
public class OrdersController {}
#PostMapping(value = "create", produces = "text/plain")
private String createOrder(#RequestBody POCreationRequest request) throws ParseException {
The API request running forever and dont get any response. I found the exception in my log. Any idea on this issue?
You created two urls there:
url/orders/ -> accepts get/post/etc... (though its not implemented)
url/orders/create -> accepts post
#RestController
#RequestMapping("/orders")
public class OrdersController {
#PostMapping(value = "create", produces = "text/plain")
private String createOrder(#RequestBody POCreationRequest request) throws ParseException {
System.out.println(request)}
}
You can try the above code.
You are trying to make a GET request on an only POST endpoint, thus then not loading the page. Your endpoint should be of type GET. You can also have the same endpoint for GET and POST requests as follows:
#RestController
#RequestMapping("/orders/")
public class OrdersController {}
#PostMapping(value = "create", produces = "text/plain")
private String createOrder(#RequestBody POCreationRequest request) throws ParseException {
//Parse post requests
}
#GetMapping(value= "create")
private String servePage() {
return create; //create is the name of the html view.
}
Now when going to localhost:8080/orders/create it should serve the view.
You can also make the GET mapping return a JSON object by:
#GetMapping(value= "create")
private String serveJSON() {
return "{\"hello\": \"world\"}"; //The string is treated as JSON and not as a view.
}

redirect to POST method in same controller

I have a spring RestController and want to redirect to POST method in same controller with RequestBody.
any solution welcomes, not only redirect.
MyController:
#RequestMapping(value = "/addCompany", method = RequestMethod.POST)
public String addCompany(#Valid Company company, BindingResult result,
HttpServletRequest request, Model model) throws Exception {
//some logic
//need to pass Company Object as RequestBody
return "redirect:/app/postmethod/";
}
//Method to redirected
#RequestMapping(value = "/postmethod", method = {RequestMethod.POST, RequestMethod.GET})
public String getData( #RequestBody(required=false) Company compnay, HttpServletRequest request, Model model) throws Exception {
//some logic
//required company object
return "htmlpage";
}
I need to redirect my request to /postmethod from addCompany method in the same controller, I am open to use any feasible solution.
Check here:
https://www.baeldung.com/spring-redirect-and-forward#redirecting-an-http-post-request
As per HTTP 1.1 protocol reference, status codes 301 (Moved
Permanently) and 302 (Found) allow the request method to be changed
from POST to GET. The specification also defines the corresponding 307
(Temporary Redirect) and 308 (Permanent Redirect) status codes that
don't allow the request method to be changed from POST to GET.
#PostMapping("/redirectPostToPost")
public ModelAndView redirectPostToPost(HttpServletRequest request) {
request.setAttribute(
View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
return new ModelAndView("redirect:/redirectedPostToPost");
}
#PostMapping("/redirectedPostToPost")
public ModelAndView redirectedPostToPost() {
return new ModelAndView("redirection");
}
The request body will be passed. Here is an example using your code:
#RestController
#RequestMapping("app")
public class TestController {
#PostMapping("/addCompany")
public ModelAndView addCompany(#RequestBody Company company, HttpServletRequest request) {
System.out.println("First method: " + company.name);
request.setAttribute(
View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
return new ModelAndView("redirect:/app/postmethod/");
}
#PostMapping("/postmethod")
public void getData(#RequestBody Company company) {
System.out.println("Redirected: " + company.name);
}
public static class Company {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
When using POST request to http://localhost:8080/app/addCompany with body {"name": "Test Company"}, in the output I receive next:
First method: Test Company
Redirected: Test Company
I found a good explanation on this page here.
For protecting users against inadvertently (re)submitting a POST transaction which they had not intended, or submitting a POST into a context which they would not have wanted.
So you can set the data in session and using get method to call post method if you want to do this.

Call controller from another controller with object as a parameter and retrieve the response

I have two restcontrollers. Both are postmapping endpoints.
After the first controller (Controller1) makes everything i need witn an object, i would like to call/redirect the second controller in order to proceed and then get a response from it.
#RestController
public class Controller1{
#PostMapping("/endpoint1")
public ReponseEntity<?> doWhatController1HasToDo(#RequestBody Object request){
//some processing
//here i would like to call second controller
}
}
#RestController
public class Controller2{
#PostMapping("/endpoint2")
public ReponseEntity<?> doWhatController2HasToDo(#RequestBody Object request){
//some processing
return new ResponseEntity<>(JSON, HttpStatus.OK);
}
}
I've tried using RestTemplate, but do get always 405 error. I've read somewhere, that it's because of the multipart
private ResponseEntity forwardUsingRestTemplate(HttpServletRequest httpServletRequest, Object object) throws MalformedURLException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity req = new HttpEntity(object, headers);
RestTemplate template = new RestTemplate();
ResponseEntity<TdmResponse> response = template.exchange(getBaseUrl(httpServletRequest) + "/endpoint2", HttpMethod.POST, req, TdmResponse.class);
}
The question is, how do i call second endpoint?
Why would you need to call another endpoint? Are these controllers in separate applications?
If not it's way more efficient to use a service on top of those 2 controllers:
public class Service {
public Object processController1(Object object) {
//some processing
return processController2(result of some processing);
}
public Object processController2(Object object) {
// processing
}
}
And then in your controllers use these 2 methods:
public class Controller1{
private Service service;
public ReponseEntity<?> doWhatController1HasToDo(#RequestBody Object request){
return new ResponseEntity<>(service.processController1(request), OK);
}
}
public class Controller2{
private Service service;
public ReponseEntity<?> doWhatController1HasToDo(#RequestBody Object request){
return new ResponseEntity<>(service.processController2(resultProcess1), OK);
}
}
If there are 2 different applications then the problem might be your CSRF settings. If you have CSRF enabled in the second application then it will reject your call because you are not passing the CSRF token with RestTemplate.
Later Edit:
You can use the facade pattern to add another layer of abstraction between Controller and Services:
public class Facade{
private Service1 service1;
private Service2 service2;
public ReponseEntity<?> doWhatController1HasToDo(#RequestBody Object request){
Object resultService1 = service1.process(request);
Object resultService2 = service2.process(resultService1);
return new ResponseEntity<>(resultService2, OK);
}
public ReponseEntity<?> doWhatController2HasToDo(#RequestBody Object request){
Object resultService2 = service2.process(request);
return new ResponseEntity<>(resultService2, OK);
}
}
You could also inject the second controller into the first controller and just call the method directly. This works if you don't want to change the endpoints dynamically.
Your controller is annotated with #RestController which means whatever is returned from controller methods are interpreted in the form of json or xml. In your case, if you return anything from doWhatController1HasToDo of Controller1, it will process this as json or xml. You should do this in following ways. It might help you.
#Controller
public class Controller1{
#PostMapping("/endpoint1")
public String doWhatController1HasToDo(#RequestBody Object request){
//some processing
return "redirect:/doWhatController2HasToDo";
} }
I am assuming, both your Controller are in same folder (i.e Controller1 and Controller2). This will call method doWhatController2HasToDo(#RequestBody Object request) of Controller2, and you can do anything into this method, like
#RestController
public class Controller2{
#PostMapping("/endpoint2")
public ReponseEntity<?> doWhatController2HasToDo(#RequestBody Object request){
//some processing
return new ResponseEntity<>(JSON, HttpStatus.OK);
}
}

Spring Boot - how to handle UnrecognizedPropertyException from Jackson manually?

So I have a controller that takes input a request body:
#RestController
#RequestMapping("/foo/bar")
public class FooBarController {
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> doTheFoo(#RequestBody MyDto dto) {
...
}
}
And I have this FooBar:
public clas FooBar {
#JsonProperty("foo")
private String foo;
public void setFoo(String foo) { ... }
public String getFoo() { ... }
}
And I have set in my properties to fail on unknown properties:
spring.jackson.deserialization.fail-on-unknown-properties: true
And I also have a handler to try and capture the failure:
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(UnrecognizedPropertyException.class)
public ResponseEntity<?> handleUnrecognizedPropertyException(UnrecognizedPropertyException ex) {
...
}
}
But when I POST to my endpoint with an unknown property (e.g. {"bar": "baz"}), nothing in the GlobalExceptionHandler is run and instead a vanilla 400 BAD REQUEST is returned. How do I intercept the unknown property exception and provide a different response?
if you are using properly the property and your controller works well, you could intercept the HttpMessageNotReadableException exception.
Try BasicErrorController of Spring Boot. This should look something like this.
#Controller
#RequestMapping
public class ErrorController {
#RequestMapping( value = "/error/404.html" )
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public String pageNotFoundError( HttpServletRequest request ) {
"errors/404"
}

Parsing JSON with RequestBody in Spring

So I've been having some issues with #RequestBody in Spring. I've had no problems at all when it comes to returning objects and having Spring automatically parse them. I have no issue if I only declare a String as an argument for the method. Here is the code, I'll begin with what I want to receive:
public class ToParse {
private String name;
ToParse() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Here is the controller:
#RestController
#RequestMapping("/test")
public class ConcreteTestController implements TestController {
#RequestMapping(method = RequestMethod.POST, consumes = {"application/json"})
#ResponseStatus(value = HttpStatus.OK)
#Override
public ToParse getSilly(#RequestBody ToParse toParse) {
ToParse toReturn = new ToParse();
toReturn.setName("stuff");
return toReturn;
}
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
#Override
public ToParse getSilly(String test) {
ToParse toReturn = new ToParse();
toReturn.setName(test);
return toReturn;
}
}
Note that the second method works just fine. The header I am sending in is "application/json" for the first method and the JSON is:
{
“name”:”blablabla”
}
I get a 415 error when trying to call the first method using the rest client in intelliJ. My configuration is in XML and it is .
What am I doing wrong? There are many answers to these types of question but usually they are resolved by fixing header or Springconfig. I cannot see what I have done wrong.
You are getting a HTTP Error 415 Unsupported media type because you are sending a POST request without adding the Content-Type: application/json header.
The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17

Categories