As editing a question didn't seem to bump it up for more answers, I'll continue asking in a new thread.
The thing is that I'm trying to set up a very basic web-service with Spring, to which I should be able to send JSON through Java.
But I'm very new to Spring, and web-services in general, so I don't know where to troubleshoot.
Old question: Getting a RESTful webservice with Spring to understand a JSON-string
Right now I have this in my controller
#RequestMapping(value = "/toggleB")
public #ResponseBody String sendCommand(#RequestBody Ident ident) {
//body
}
The Ident-class just has a String as variable, called IP.
Sending this String
{"IP":"192.168.1.9"}
Gives the return code 400.
Thing is, I think there might be something wrong, or missing, in the build.gradle (or pom.xml).
How is a correct Gradle/Spring project supposed to be organized as?
My main method is runnable, and looks like this:
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
#ComponentScan
#EnableAutoConfiguration
public class mainFile {
public static void main(String[] args) {
SpringApplication.run(mainFile.class, args);
}
}
I don't use any beans as configuration, might that be what fails my program?
Thanks again for replies!
I hope I'm not breaking the rules by continuing my question like this, but I didn't know how to bump the old one up.
Ah... The disadvantages of being completely new to a site, heh.
If you're sending JSON in the request body, make it a POST or a PUT request, also add headers to accept json. Fetch the Json as a string and then use a Json parser to parse it to a JsonNode or whatever you want. And at last return a ResponseEntity. Below is a sample code. Do ask specific questions in the comment for clarification if needed.
#RequestMapping(value = "/toggleB", method = RequestMethod.POST, headers = "Accept=application/json")
public ResponseEntity<java.lang.String> sendCommand(#RequestBody String ident) {
// Do your stuff
return new ResponseEntity<String>("Some response data/message",new HttpHeaders(), HttpStatus.OK);
}
Related
I am implementing a mechanism for replacing short links.
I need to forwarded request to another controller. I found examples how to do it in spring on models, but I don't understand how to do it in RestControllers
Example what i found (use models)
#Controller
public class ShrotLinkForwardController {
#RequestMapping("/s/*")
public String myMethod(HttpServletRequest request) {
return "forward:/difmethod";
}
}
Or maybe I'm looking in the wrong direction and I need to make a filter?
UPD. I don't know the final endpoint, it is calculated in the forwarded method. So, i cant autowired other controller
There are 2 ways to achieve what you want.
1. Call the method on the target controller directly.
Controllers are just normal Spring beans. You can get it via autowire.
#Controller
public class ShrotLinkForwardController {
#Autowired
OtherController otherController;
#RequestMapping("/s/*")
public String myMethod(Model model) {
otherController.doStuff();
return ...;
}
}
2. Trigger the forward by returning a string
To trigger the forward, try returning a String instead of ModelAndView.
This is the approach you mentioned in your question. Note that the syntax should be forward:/forwardURL. The string after forward: is the URL pointing to another controller, not the method name.
#Controller
public class ShrotLinkForwardController {
#RequestMapping("/s/*")
public String myMethod(Model model) {
return "forward:/forwardURL";
}
}
you could inject the target controller and simply call the method
#Controller
public class ShortLinkForwardController {
#Autowired
private RestController target;
#RequestMapping("/s/*")
public String myMethod(HttpServletRequest request) {
return target.myMethod(request);
}
}
Caveat: Path related request properties will still point to "/s/*"
Or use ResponseEntity and set target location...
public ResponseEntity<Void> myMethod(HttpServletRequest request) {
return ResponseEntity.status(302).location(URI.create(...)).build();
}
All answers are about returning String
But I've found another solution
Maybe it will help someone with my problem in case when you need to make forward from one REST endpoint to another REST endpoint.
And it also could be applied to your case.
#RestController
public class CustomerController {
#GetMapping("/forwarding_endpoint")
public void makeForward(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getSession().getServletContext().getRequestDispatcher("/forward_endpoint").forward(request, response);
}
}
UPD. I don't know the final endpoint, it is calculated in the
forwarded method. So, i cant autowired other controller
but I don't understand how to do it in RestControllers
I can see some indications of possible bad design here, so I will try to explain the possible issues and how should be handled according to best practices.
If your requirement is to make a forward to another controller, then this might be an indication of 3 possible issues:
The job to be done by the other controller (which you say you want to forward to) can be extracted into a service method in service layer. Then both controllers can call the same service method, without each controller be aware of the other.
Your need could also be an indicator of the following issue. You need 2 controllers for exactly the same practical reason, so that they provide for same input exactly the same output, but to be available from 2 different URLs. If this is the case then you can use just 1 controller and allow it to be executed for both URLs. See the following code to achieve this:
#RequestMapping({"/s/*", "/s2/*})
public String myMethod(HttpServletRequest request) {
return "some response";
}
You need to expose only 1 URL to the client which will serve everything. Then the approach with forward will also not benefit you, since the client will be able to reach the other forwarded controller directly if he wishes so. In this case you can implement 1 single controller which then according to the needs builds different responses. You can do this in RestController although not suggested by Sonar and other code review tools by marking the method to return ResponseEntity<?>. Example:
#RequestMapping("/s/*")
public ResponseEntity<?> myMethod(HttpServletRequest request) {
if (condition 1) {
return new ResponseEntity<YourObject1>(HttpStatus.OK);
} else if (condition 2) {
return new ResponseEntity<YourObject2>(HttpStatus.OK);
} else {
return new ResponseEntity<YourObject3>(HttpStatus.OK);
}
}
this last choice is not considered best practice with <?> but for this requirement I don't see any other way out.
I am using jersey rest service in java to accept request.
Here is my snippet
#Path("main")
public class xxxx{
#GET
#Path("test/{path}")
public void test(#Context HttpServletRequest req ) {
System.out.println(req.getRequestURI());
}
}
I am invoking this using REST Api as test/abcd , it is working. I want #path to accept test/abcd or test/abcd/ab and so. I tried with "test/{path}/*" nothing works.
Please someone help me as I am new to this.
You should use regex in the #Path for example :
#Path("{parameter: .*}")
Response getData(#PathParam("parameter") List<String> parameter){
//do processing
}
For more details you can see the examples given here.
Let's say I have a controller that has a variety of endpoints (GET/POST/PUT/DELETE) and generally they both produce and consume JSON, so I do:
#RestController
#RequestMapping(value=["/some/base/path"], produces = [MediaType.APPLICATION_JSON_UTF8_VALUE], consumes = [MediaType.APPLICATION_JSON_UTF8_VALUE])
public class SomeController {
...
}
But it turns out that my #GetMapping does not consume JSON (and I don't want to force callers to set Content-Type: application/json for GET requests. Is there a way, on the #GetMapping, to clear/empty the consumes = value that was set at the class level? Or is there another way to avoid repeating the consumes attribute on all methods in the class?
I've already tried setting the #GetMapping(value=["/some/path"], consumes = []) without any luck. For context, I'm converting from Jersey annotations to Spring REST controller style annotations and I'm finding this to be an annoying difference in behavior (setting a class-level #Consumes annotation doesn't get enforced against #GETs). And just looking for an elegant way to mirror existing behavior without cloning the consumes attribute all over the place.
I got your problem now .Try use the below solution and refer to this link that might help
https://github.com/spring-projects/spring-framework/pull/1257/commits/00e6ca412dffeb8a7a596f9312db19eb6cc49525
#GetMapping(value = "/get", consumes = MediaType.ALL_VALUE)
For your case , you need to delete the consumes part.I mean just use the Produces only.For example :
#GET
#Produces("application/json")
#Path("/{oid}")
public Book getBook(#PathParam("oid") String oid) {
return bookService.getBook(oid);
}
or check this url :
https://dzone.com/articles/spring-boot-building-restful-web-services-with-jersey
Is there anyway to cache the response of spring REST API by method parameter? For example, in below code, return the same response (the json serialized data) from cache if the country is already retrieved once.
#Controller
public class DataController {
// Can we cache here by country?
#RequestMapping(value = "/api/info/{country}", method = RequestMethod.GET)
public CountryInfo getCountryInfo(#PathVariable("country")String country){
return service.getCountryInfo(country);
}
}
There is a good guide that shows how you can cache data with Spring.
Even though my answer does not include any code, I would recommend taking a look at it. It is a step-by-step guide that I believe can help you with your problem.
Hi SO.
I have a weird problem that I've been struggling with all day long.
I have my Angular JS module (App), and a controller. In that controller, I inject a factory, which handles my $http.post method. (Which i call "doSearch"). So far so good.
On the back-end I have a spring-mvc rest api class. It looks like this:
#Controller
#RequestMapping("/api/filesearch")
public class FileSearchAPI {
#RequestMapping(value = "/search", method = RequestMethod.POST)
#ResponseBody
public ResultDTO doSearch(#RequestBody NewPerfectSearchDTO searchDTO) {
System.out.println(searchDTO.getIdentifier());
SolrService solrService = new SolrService();
//return solrService.query(searchDTO);
return new ResultDTO();
}
}
And this is the function that sends some JSON, that will be mapped to a JAVA POJO/DTO:
doSearch: function(searchParams) {
$http.post("/np/api/filesearch/search", JSON.stringify({identifier: "apa", inputRows: []})).success(function(response) {
console.log(response);
return response;
}).error(function(data, status, headers, config) {
console.log(status, headers, config);
});
}
Take note of the payload (Yes, it's static for this example):
JSON.stringify({identifier: "apa", inputRows: []})
This maps perfectly fine in my DTO. The fields are correct, and variable inputRows is an empty, initialized array.
HOWEVER when I look at the payload (Using firebug or the chrome inspector) , sent by the client, to the API,
it looks like this: {"identifier":"apa","inputRows":"[]"}
Did you notice the "" around my JSON array?
{"identifier":"apa","inputRows":"[]"}
^ ^
| |
Yeah... That's the problem I'm having.
When I send that request, the server simply replies with a HTTP 400, malformed syntax (Tomcat/Spring error page), the request doesn't even invoke the APIController on the back-end.
So, what I've tried so far (without success):
I have tried without using JSON.stringify() on the payload, with
the same result
I have tried specifying the headers in my APIController (No use, it doesn't even execute that code before throwing the error)
I have tried using a Java.util.List instead of an array in the DTO
So I'm pretty much stuck here. Any help would be appreciated.
It sounds like you have a conflicting prototype.js dependency overriding some functions.
I would try
delete Array.prototype.toJSON
before posting your object.