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.
Related
I have an API controller:
#RestController
#RequestMapping("api")
public class ListingRestController {
#PostMapping("/listings/edit/{id}")
public void editListing(ListingForm newListing, #PathVariable Integer id, Model model) {
ListingDto newListingDto = new ListingDto(newListing.getId(), newListing.getUserId(), newListing.getTitle());
model.addAttribute("submitURL", String.format("edit/%s", id));
listingService.deleteListingById(id);
listingService.addListing(newListingDto);
}
}
It takes a POST request from api/listings/edit/{id}. Also it should get the data from form inputs ListingForm newListing.
The problem is, the form is defined in a route listings/edit/{id}, so the controller method cannot get data. Any idea how I can get form data from route listings/edit/{id} in my API controller?
You must define GET api beside Api Post to get data. First, client call API GET to get data from server and place data to the form. After finish editing data, call API POST to post data to server.
You can write #RequestBody annotation to ListeningForm parameter
Background
We are consuming the API from a 3rd-party vendor
Problem Statement:
I am building a wrapper API around another API. The same exact JSON payload that I will be receiving from the client to the wrapper API will also be used to make an HTTP request to the original API.
Currently I'm converting JSON which deserializes to a String. Is this the right approach if the payload is just passing through the wrapper API to the original API? In other words, is the #RequestBody type String okay for my use case or do I still need to deserialize to a Java Object?
Use Case for wrapper
If multiple teams consumed the API from the 3rd-party vendor, all teams would have to make changes if we were to switch vendors. If we create a wrapper, only one team would have to make the changes. There is no processing in this wrapper.
Controller Code:
#RestController
#RequestMapping(value = FolderController.PATH, produces = MediaType.APPLICATION_JSON_VALUE)
public class PersonController(){
static final String PATH = "/person";
private final PersonService personService;
#Autowired
public PersonController(PersonService personService){
this.personService = personService
}
#PostMapping
#ResponseBody
public String createPerson(#RequestBody String requestBody){
return personService.createPerson(requestBody);
}
Whether you need to deserialize depends on what processing is necessary for your wrapper. If you just want to pass the request further without changing it, this should work.
However, if you just need to proxy the request, consider using Smiley's HTTP Proxy Servlet for this task.
Or if you are wrapping the API to implement security around it, then consider using Spring cloud gateway
It is always better to follow coding practices and convert the request body to a java POJO class instead of a string. Converting to POJO has several advantages :
You could provide additional validations for the request body.
Incase the request body changes or if there are any issues it could be identified easily.
Provides better control over the request structure.In future , if the API that you are consuming changes the request body structure, you could change it from your API instead of adapting it at every API that consumes your API.
I am building a RESTful web service that can be consumed by a browser or another web service.
I am willing to reduce the bandwidth through caching, however i want the method to be executed and send the actual data only if it's different than the last modified cache.
From my understanding of the #cacheable annotation, the method is only executed once and the output is cached until the cache expires .
Also #CachePut executes everytime and updates the cache but does it send the cache again even if it's not updated?
summary is: i need the client to be able to send the last modified date of it's cache and only get a new data if it has been modified.
Also how does Spring handle the client side caching and if-modified-since headers? does i need to save the last modified time or it is automatically handled ?
No, you need to do it by yourself.
You need to annotate your "fetch" method with #Cacheable(docs) and then, annotate "update" method with #CacheEvict (docs) in order to "drop" your cache. So when you would fetch your data next time after its modification, it will be fresh.
Alternatively, you can create another method with #CacheEvict and manually call it from "update" method.
The cache related annotations (#Cacheable, #CacheEvict etc) will only deal with the cache being maintained by application. Any http response header like last-modified etc has to be managed seperately. Spring MVC provides a handy way to deal with it (docs).
The logic to calculate the last modified time has to be obviously application specific.
An example of its usage would be
MyController {
#Autowire
CacheService cacheService;
#RequestMapping(value = "/testCache", method = RequestMethod.GET)
public String myControllerMethod(WebRequest webRequest, Model model, HttpServletResponse response) {
long lastModified = // calculate as per your logic and add headers to response
if (request.checkNotModified(lastModified)) {
// stop processing
return null;
} else {
return cacheService.getData(model);
}
}
#Component
public class CacheService{
#Cacheable(value = "users", key = "#id")
public String getData(Model model) {
//populate Model
return "dataview";
}
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
Here is the controller method which i'm trying to redirect from:
#GetMapping("/opensignup/{id}")
public String openSignUp(#PathVariable(value = "id") Long Id) {
Tournament comp = tournamentRepository.findOne(Id);
boolean b = true;
comp.setSignUpOpen(b);
return "redirect:/comp/" + Id;
}
I have another method (which is a post, if thats relevant) which successfully redirects to the correct page/controller with the same line:
return "redirect:/comp/" + Id;
Instead of redirecting, it simply prints that link on the browser. (It prints it with the correct id.) Like this:
redirect:/comp/5
How do I get it to redirect instead of print?
As stated in the comments, your second method is annotated with #RestController. While #Controller is meant to return views in the Spring context, #RestController is meant to return something that will be written to the response body directly (JSON in most cases).
#RestController is basically a combination of the #Controller AND a #ResponseBody annotation, whereby the second will try to map the POJO to JSON.
Further reading:
https://www.genuitec.com/spring-frameworkrestcontroller-vs-controller/
Difference between spring #Controller and #RestController annotation
This was happening because the top of my method had an #RestController annotation instead of #Controller. Since I don't need it to be restful, I changed it and my problem is solved. If anyone wants to shed some light on why this is and/or how you would do it while keeping the #RestController annotation (if possible) then feel free.