Passing pageable (spring data) in post request - java

I have a rest API server which has the following API.
I have some other APIs, where i get pageable from GET requests. Here, I need to make a post request for passing the queryDto. So, I cannot pass the page=0?size=20 etc as url parameters.
I would like to know how to pass pageable as JSON object to a POST request
#RequestMapping(value = "/internal/search", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public ResponseList<Object> findObjects(#RequestBody QueryDto queryDto, Pageable pageable) {
if (queryDto.isEmpty()) {
throw new BadRequestException();
}
return someService.findObjectsByQuery(queryDto, pageable);
}

I Think that is not possible, at least not already provided by the framework.
The Spring has a HandlerMethodArgumentResolver interface with an implementation called PageableHandlerMethodArgumentResolver that retrieves the request param value calling something like HttpServletRequest.getParameter. So, you can bind the Pageable instance passing the parameters "page" and "size" for GET and POST. So, the following code works:
#RequestMapping(value="/test",method = RequestMethod.POST)
#ResponseBody
public String bindPage(Pageable page){
return page.toString();
}
$ curl -X POST --data "page=10&size=50" http://localhost:8080/test
Return:
Page request [number: 10, size 50, sort: null]
But, if you pass an json nothing happens:
$ curl -X POST --data "{page:10&size:50}" http://localhost:8080/test
Return:
Page request [number: 0, size 20, sort: null]

Spring Post method
#RequestMapping(value = "/quickSearchAction", method = RequestMethod.POST)
public #ResponseBody SearchList quickSearchAction(#RequestParam(value="userId") Long userId,
Pageable pageable) throws Exception {
return searchService.quickSearchAction(userId, pageable);
}
Postman Example:
http://localhost:8080/api/actionSearch/quickSearchAction?
userId=4451&number=0&size=20&sort=titleListId,DESC
In above POST Pageable is used for Sorting and Pagination in Spring RESTful service. Use below syntax at URL.
number 0, size 20, Sort by field titleListId and direction DESC
All passing parameter internally recognizes by Pageable as Sorting / Pagination parameters as below
number - Page number
size - Page Size
sort - sort by(Order by)
direction - ASC / DESC
Updated:
Angular Example:
CustomerComponent.ts file
let resultDesignations = null;
let fieldName = "designationId";
this.customerService.getDesignations(fieldName, "DESC").subscribe(
(data) => {
resultDesignations = data;
},
(err) => {
this.error(err.error);
},
() => {
this.designations = resultDesignations;
}
);//END getDesignations`
CustomerService.ts
getDesignations(fieldName: string, sortOrder: string): Observable<any> {
return this.httpClient.get("http://localhost:9876/api/getDesignations", {
params: {
sort: fieldName,sortOrder
}
});
}

It seems to work just fine for me if you continue to provide them as query parameters on the URL, and still post data in.
POST http://localhost:8080/xyz?page=2&size=50
Content-Type: application/json
{
"filterABC": "data"
}
Spring seems to translate the page, size, sort etc. into the Pageable provided to the method on the way in.

create a class that has the Pageable and QueryDto objects as members. Then pass JSON in the post body of this new object.
for example,
public class PageableQueryDto
{
private Pageable pageable;
private QueryDto queryDto;
... getters and setters.
}
Edit
As noted in the comment below, you may need to implement the Pageable interface.
The result could be something like this:
public class PageableQueryDto implements Pageable
{
private Pageable pageable;
private QueryDto queryDto;
... getters and setters.
... Implement the Pageable interface. proxy all calls to the
... contained Pageable object.
... for example
public void blam()
{
pageable.blam();
}
... or maybe
public void blam()
{
if (pageable != null)
{
pageable.blam();
}
else
{
... do something.
}
}

sample example
#RequestMapping(path = "/employees",method = RequestMethod.POST,consumes = "application/json",produces = "application/json")
ResponseEntity<Object> getEmployeesByPage(#RequestBody PageDTO page){
//creating a pagable object with pagenumber and size of the page
Pageable pageable= PageRequest.of(page.getPage(),page.getSize());
return ResponseEntity.status(HttpStatus.ACCEPTED).body(employeeRegistryService.getEmployeesByPage(pageable));
}
In your case try to add pagination variables in QueryDTO
create a Pageable object and pass it to service
I think that will solve :)

Related

How to use Pageable as get-query parameter in spring-data-rest?

I have a simple query controller that takes query parameters (as Person dto) and a Pageable:
#RestController
public class PersonController {
#GetMapping("/persons")
public Object search(org.springframework.data.domain.Pageable pageable, Person form) {
repository.findAll(form, pageable);
}
}
The pageable can take both sort and page parameters as follows:
http://localhost:8080/persons?sort=age,desc&page=5
Problem: for the sort, I want to add order hints like NULLS_LAST or NULLS_FIRST.
Question: how can I achieve this using query params too?
You can modify Pageable object like this (method to put in your controller for example). This is an example with one sort field, but you can loop on all fields and modify nullsLast/nullsFirst in the same way. Please try :
private Pageable customSort(Pageable pageable) {
Sort sort = pageable.getSort();
Order order = sort.iterator().next();
List<Order> orders = new ArrayList<>();
// nullsFirst or nullsLast
orders.add(new Order(order.getDirection(),order.getProperty()).nullsFirst());
return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(orders));
}
Try to do something like :
pageable.getSort().and(Sort.by(new Sort.Order(Sort.Direction.DESC, "age", Sort.NullHandling.NULLS_LAST)));

How to map path params to gRPC object in spring based REST API?

My REST API must work with gRPC objects as input parameters.
The most simple example is:
GET http://localhost:8083/api/books?page.number=1&page.size=30
where the proto definition is:
message PagedMessage {
Page page = 1;
}
message Page {
int32 number = 1;
int32 size = 2;
}
The controller is:
#RequestMapping(value = "/api/books")
public class ObjectOps {
#Autowired
private BooksService booksService;
#GetMapping(value = "/")
#ResponseBody
BooksList listBooks(#RequestParam PagedMessage request) {
return booksService.getBooks(request);
}
}
And in the application I have this bean:
#Bean
ProtobufJsonFormatHttpMessageConverter protobufJsonFormatHttpMessageConverter() {
return new ProtobufJsonFormatHttpMessageConverter();
}
The only way it worked for me is to pass the paging information as GET body:
{
"page" : {
"number": 1,
"size": 30
}
}
but it will be great to have the list books method object be populated from the request path parameters.
I think you can just remove the #RequestParam annotation and Spring will populate the object.
Referenced by this answer: https://stackoverflow.com/a/16942352/8075423

Can we use swagger annotation values in our java code

I am new to java. Trying to develop a application to schedule http api calls in a cron job. Only the method name will be the input. All the apis are configured with swagger annotations. Can I use these annotations to determine whether the api is post or get or delete etc. For example
public class ABC {
#ApiOperation(
httpMethod = "GET",
value = "value",
notes = "notes",
response = ABC.class)
ABC getValue()
{
}
}
only getValue is the input to my application. Can I get the #ApiOperation values to determine the http method type.
You can, but it is in the RequestMapping annotation (the one where you specify which URL should be linked to the method):
For example, this method will be called when someone navigates to myBaseURl/persons in GET. It will return JSON.
#ApiOperation( value = "List of persons",
notes = "List all my base's persons. ",
response = Person.class,
responseContainer = "List",
tags = { "Persons", })
#RequestMapping(value = "/persons",
produces = { "application/json" },
method = RequestMethod.GET)
public PagedResources<PersonResource> persons(...) {}

Spring MVC - How to return simple String as JSON in Rest Controller

My question is essentially a follow-up to this question.
#RestController
public class TestController
{
#RequestMapping("/getString")
public String getString()
{
return "Hello World";
}
}
In the above, Spring would add "Hello World" into the response body. How can I return a String as a JSON response? I understand that I could add quotes, but that feels more like a hack.
Please provide any examples to help explain this concept.
Note: I don't want this written straight to the HTTP Response body, I want to return the String in JSON format (I'm using my Controller
with RestyGWT which requires the response to be in valid JSON
format).
Either return text/plain (as in Return only string message from Spring MVC 3 Controller) OR wrap your String is some object
public class StringResponse {
private String response;
public StringResponse(String s) {
this.response = s;
}
// get/set omitted...
}
Set your response type to MediaType.APPLICATION_JSON_VALUE (= "application/json")
#RequestMapping(value = "/getString", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
and you'll have a JSON that looks like
{ "response" : "your string value" }
JSON is essentially a String in PHP or JAVA context. That means string which is valid JSON can be returned in response. Following should work.
#RequestMapping(value="/user/addUser", method=RequestMethod.POST)
#ResponseBody
public String addUser(#ModelAttribute("user") User user) {
if (user != null) {
logger.info("Inside addIssuer, adding: " + user.toString());
} else {
logger.info("Inside addIssuer...");
}
users.put(user.getUsername(), user);
return "{\"success\":1}";
}
This is okay for simple string response. But for complex JSON response you should use wrapper class as described by Shaun.
In one project we addressed this using JSONObject (maven dependency info). We chose this because we preferred returning a simple String rather than a wrapper object. An internal helper class could easily be used instead if you don't want to add a new dependency.
Example Usage:
#RestController
public class TestController
{
#RequestMapping("/getString")
public String getString()
{
return JSONObject.quote("Hello World");
}
}
You can easily return JSON with String in property response as following
#RestController
public class TestController {
#RequestMapping(value = "/getString", produces = MediaType.APPLICATION_JSON_VALUE)
public Map getString() {
return Collections.singletonMap("response", "Hello World");
}
}
Simply unregister the default StringHttpMessageConverter instance:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
/**
* Unregister the default {#link StringHttpMessageConverter} as we want Strings
* to be handled by the JSON converter.
*
* #param converters List of already configured converters
* #see WebMvcConfigurationSupport#addDefaultHttpMessageConverters(List)
*/
#Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(c -> c instanceof StringHttpMessageConverter);
}
}
Tested with both controller action handler methods and controller exception handlers:
#RequestMapping("/foo")
public String produceFoo() {
return "foo";
}
#ExceptionHandler(FooApiException.class)
public String fooException(HttpServletRequest request, Throwable e) {
return e.getMessage();
}
Final notes:
extendMessageConverters is available since Spring 4.1.3, if are running on a previous version you can implement the same technique using configureMessageConverters, it just takes a little bit more work.
This was one approach of many other possible approaches, if your application only ever returns JSON and no other content types, you are better off skipping the default converters and adding a single jackson converter. Another approach is to add the default converters but in different order so that the jackson converter is prior to the string one. This should allow controller action methods to dictate how they want String to be converted depending on the media type of the response.
I know that this question is old but i would like to contribute too:
The main difference between others responses is the hashmap return.
#GetMapping("...")
#ResponseBody
public Map<String, Object> endPointExample(...) {
Map<String, Object> rtn = new LinkedHashMap<>();
rtn.put("pic", image);
rtn.put("potato", "King Potato");
return rtn;
}
This will return:
{"pic":"a17fefab83517fb...beb8ac5a2ae8f0449","potato":"King Potato"}
Make simple:
#GetMapping("/health")
public ResponseEntity<String> healthCheck() {
LOG.info("REST request health check");
return new ResponseEntity<>("{\"status\" : \"UP\"}", HttpStatus.OK);
}
Add produces = "application/json" in #RequestMapping annotation like:
#RequestMapping(value = "api/login", method = RequestMethod.GET, produces = "application/json")
Hint: As a return value, i recommend to use ResponseEntity<List<T>> type. Because the produced data in JSON body need to be an array or an object according to its specifications, rather than a single simple string. It may causes problems sometimes (e.g. Observables in Angular2).
Difference:
returned String as json: "example"
returned List<String> as json: ["example"]
Add #ResponseBody annotation, which will write return data in output stream.
This issue has driven me mad: Spring is such a potent tool and yet, such a simple thing as writing an output String as JSON seems impossible without ugly hacks.
My solution (in Kotlin) that I find the least intrusive and most transparent is to use a controller advice and check whether the request went to a particular set of endpoints (REST API typically since we most often want to return ALL answers from here as JSON and not make specializations in the frontend based on whether the returned data is a plain string ("Don't do JSON deserialization!") or something else ("Do JSON deserialization!")). The positive aspect of this is that the controller remains the same and without hacks.
The supports method makes sure that all requests that were handled by the StringHttpMessageConverter(e.g. the converter that handles the output of all controllers that return plain strings) are processed and in the beforeBodyWrite method, we control in which cases we want to interrupt and convert the output to JSON (and modify headers accordingly).
#ControllerAdvice
class StringToJsonAdvice(val ob: ObjectMapper) : ResponseBodyAdvice<Any?> {
override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean =
converterType === StringHttpMessageConverter::class.java
override fun beforeBodyWrite(
body: Any?,
returnType: MethodParameter,
selectedContentType: MediaType,
selectedConverterType: Class<out HttpMessageConverter<*>>,
request: ServerHttpRequest,
response: ServerHttpResponse
): Any? {
return if (request.uri.path.contains("api")) {
response.getHeaders().contentType = MediaType.APPLICATION_JSON
ob.writeValueAsString(body)
} else body
}
}
I hope in the future that we will get a simple annotation in which we can override which HttpMessageConverter should be used for the output.
Simple and Straightforward send any object or return simple List
#GetMapping("/response2")
#ResponseStatus(HttpStatus.CONFLICT)
#ResponseBody List<String> Response2() {
List<String> response = new ArrayList<>(Arrays.asList("Response2"));
return response;
}
I have added HttpStatus.CONFLICT as Random response to show how to pass RequestBody also the HttpStatus
Annotate your method with the #ResponseBody annotation to tell spring you are not trying to render a view and simple return the string plain

FIlter data with Spring coming from request

I have some infromation coming from request such as:
http://localhost:9080/online/accounts/list/usersQuery?filter=uid&value=ab
And I have to treat this in Spring where the object is uid and the filter value is ab
So far I have the folowing code in Spring:
#RequestMapping(produces="application/json", value = "/usersQuery", method=RequestMethod.GET)
public #ResponseBody PagedResources<Resource<UserDetails>> listItemsSortQuery(#PageableDefault(size = 20, page = 0) Pageable pageable, PagedResourcesAssembler<UserDetails> assembler) {
Page<UserDetails> lstUserDetails = userDetailsRepository.findAll(pageable);
return assembler.toResource(lstUserDetails);
}
But it doesn't consider nothing about those two values.
What should I change in order to filter data according to the field uid and filter data ab ?
The uid is the user id in the user object and I need to pick all the users that have an id containing ab
Any help would be apreciated.
Try getting uid value with #RequestParam
#RequestMapping(produces="application/json", value = "/usersQuery", method=RequestMethod.GET)
public #ResponseBody PagedResources<Resource<UserDetails>> listItemsSortQuery(#PageableDefault(size = 20, page = 0) Pageable pageable, PagedResourcesAssembler<UserDetails> assembler,
#RequestParam("filter")String filter, #RequestParam("value")String value) {
Page<UserDetails> lstUserDetails = userDetailsRepository.findByFilter(pageable, filter, value);
return assembler.toResource(lstUserDetails);
}
EDITED:
In your repository you need a method to filter your data, i.e.
public interface UserDetailsRepository extends JpaRepository<UserDetails, Long> {
#Query("SELECT u FROM UserDetails u WHERE LOWER(?1) = LOWER(?2)")
Page<UserDetails> findByFilter(String filter, String value, Pageable pageable);
}

Categories

Resources