Grails: POST or GET httprequest params only - spring

In controller and gsp there are avaliable two variables:
params - tahat contains map of all params (both GET from Query String and POST from forms)
request - the HttpServletRequest object
How can I get POST only params or GET (query string) only params? I need both types of params but in different maps.
I know, that HttpServletRequest object contains information about Query String, but I'm looking for solution without implementing parser of query string.

I have solved this problem by parsing Query String with org.codehaus.groovy.grails.web.util.WebUtils and then I put these data into HttpServletRequest attribute.
Now I can use request.getAttribute('paramsGet') or request.getAttribute('paramsPost').
The Grails filters was very helpfull, so:
package com.selly.filters
import org.codehaus.groovy.grails.web.util.WebUtils
class ParamsFilters {
List globalParams = [
"controller",
"action",
"format"
]
def filters = {
all(controller:'*', action:'*') {
before = {
Map paramsRequest = params.findAll {
return !globalParams.contains(it.key)
}
Map paramsGet = WebUtils.fromQueryString(request.getQueryString() ?: "")
Map paramsPost = paramsRequest.minus(paramsGet)
request.setAttribute('paramsGet', paramsGet)
request.setAttribute('paramsPost', paramsPost)
//println request.getAttribute('paramsGet')
//println request.getAttribute('paramsPost')
}
after = { Map model ->
}
afterView = { Exception e ->
}
}
}
}

Even with Grails, you're working with Servlet which mix up POST and GET. And I don't remember seeing something (except for reparsing the Query String) which would help you.

Related

Spring cache for specific values #Cacheable annotation

I want to cache a result of a method only when the attribute of the result contains specific values. For example
Class APIOutput(code: Int, message: String)
sealed class Response<out T : Any> : Serializable {
data class Success<out T : Any>(val data: T) : Response<T>()
data class Error(val errorText: String, val errorCode: Int) : Response<Nothing>()
}
#Cacheable(
key = "api-key",
unless = "do something here"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
In the above method, I want to cache the response only when Response.Success.data.code == (long list of codes).
Please note, in the previous line data is nothing but APIOutput object. How could I achieve it using unless or any other approach. I was thinking of writing a function that takes a doApicall method result as input and would return true or false and call that method it as unless="call a method". But I'm not sure how to do it. Any help is highly appreciated.
You can specify an expression to be evaluated in unless using SpEL. The returned value is available as result so you can do something like -
#Cacheable(
key = "api-key",
unless = "#result!=null or #result.success.data.code!=200"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}
You can even use Regex in SpEL and can create custom Expression parsers if the existing functionality is not enough for your usecase.
Thanks Yatharth and John! Below is the condition that worked for me. resultcodes in the below expression is a list
#Cacheable(
key = "api-key",
unless = "!(#result instanceof T(com.abc.Response\$Success))
or (#result instanceof T(com.abc.Response\$Success)
and !(T(com.abc.APIStatus).resultCodes.contains(#result.data.code)))"
)
fun doApicall(uniqueId: Long): Response<APIOutput> {
//make API call
val output = callAPI(uniqueId)
return Response.Success(output)
}

How to get Spring Boot to map query parameters separately from form data?

#RequestMapping(value = "/**", consumes = MediaType.ALL_VALUE)
#ResponseBody
public Object mirror(HttpServletRequest req, #Nullable #RequestBody Map<String, String> form) {
...
}
I just want the plain key and value for all form data entries here but it also includes query parameters in the map.
I need to be able to tell the difference between what came from the form and what came from the query.
Getting the query parameters separately is easily done using URI parsing but it's not so easy to remove the query parameters from the form map. Especially in the case they have the same keys.
Changing the parameter to MultiValueMap adds values with the same key into an array. Using just a Map causes the query parameters to overwrite the form data with equal keys.
I found where this is happening, for the MockHttpServletRequest at least: buildRequest method:
String query = this.url.getRawQuery();
if (!this.queryParams.isEmpty()) {
String s = UriComponentsBuilder.newInstance().queryParams(this.queryParams).build().encode().getQuery();
query = StringUtils.isEmpty(query) ? s : query + "&" + s;
}
if (query != null) {
request.setQueryString(query);
}
addRequestParams(request, UriComponentsBuilder.fromUri(this.url).build().getQueryParams());
this.parameters.forEach((name, values) -> {
for (String value : values) {
request.addParameter(name, value);
}
});
It's combining the form data and query data into one map. So is there an alternative way to parse the form data ONLY or exclude query params from the map!?
From the javadoc for #RequestParam:
In Spring MVC, "request parameters" map to query parameters, form data, and parts in multipart requests. This is because the Servlet API combines query parameters and form data into a single map called "parameters", and that includes automatic parsing of the request body.
Not sure if there's a more elegant way, but you could possibly use Spring's UriComponentsBuilder class to parse the URI string and get back the query parameters.

Create instance of SpringĀ“s ParameterizedTypeReference in Kotlin

I am trying to learn Kotlin, and test how it works with spring boot. My application is using a mongo database to store data and I have a Jersey resource for retrieving data. I am testing it using spring-boot-test and RestTestTemplate.
The RestTestTemplate has an exchange method which takes a ParameterizedTypeReference. This class has a protected constructor. So the only way I managed to use it from Kotlin was like this:
class ListOfPeople : ParameterizedTypeReference<List<Person>>()
Here is my test-method:
#Test
fun `get list of people`() {
// create testdata
datastore.save(Person(firstname = "test1", lastname = "lastname1"))
datastore.save(Person(firstname = "test2", lastname = "lastname2"))
datastore.save(Person(firstname = "test3", lastname = "lastname2"))
datastore.save(Person(firstname = "test4", lastname = "lastname2"))
val requestEntity = RequestEntity<Any>(HttpMethod.GET, URI.create("/person"))
// create typereference for response de-serialization
class ListOfPeople : ParameterizedTypeReference<List<Person>>() // can this be done inline in the exchange method?
val responseEntity : ResponseEntity<List<Person>> = restTemplate.exchange(requestEntity, ListOfPeople())
assertNotNull(responseEntity)
assertEquals(200, responseEntity.statusCodeValue)
assertTrue( responseEntity.body.size >= 4 )
responseEntity.body.forEach { person ->
println("Found person: [${person.firstname} ${person.lastname}] " +
", born [${person.birthdate}]")
}
}
Is this the correct (or only) way to do this, or is there a better way?
If it helps, here is a link for the whole test: testclass on github
While the answer using object expression is correct and the direct equivalent of the way you do it in Java, reified type parameters allow you to simplify it if you need many ParameterizedTypeReferences:
inline fun <reified T> typeReference() = object : ParameterizedTypeReference<T>() {}
// called as
restTemplate.exchange(requestEntity, typeReference<List<Person>>())
When the compiler sees a typeReference<SomeType> call, it's replaced by the definition, so the result is the same as if you wrote object : ParameterizedTypeReference<SomeType>() {}.
Thanks to JB Nizet who pointed me to the correct documentation.
val responseEntity : ResponseEntity<List<Person>> =
restTemplate.exchange(requestEntity,
object: ParameterizedTypeReference<List<Person>> () {})
If I read correctly this is called an Object expression.

Springboot controller request param for map always null

I'm trying to pass in a bunch of id's to create a filter.
The incoming request looks like (ignoring the pagination stuff, which works fine)
http://localhost:8080/news-items?filter%5B%5D=09c731de-7ed8-385d-849c-f4d6535137ab&filter%5B%5D=dd1ba187-2df9-3985-ad1c-a4cde2dfe669&modelPath=controller.newsItems&page=0&per_page=25
Where the filter param equals an ID, but there is a bunch of them, for example:
filter: [
"09c731de-7ed8-385d-849c-f4d6535137ab",
"dd1ba187-2df9-3985-ad1c-a4cde2dfe669"
],
I can't seem to collect the filters in the controller. At the moment I have
public String getFeeds(#RequestParam(value = "filter", required = false) MultiValueMap<String, String> filter, #RequestParam(value = "page", required = false) int page, #RequestParam(value = "per_page", required = false) int perPage) {
log.info("Filter: {}", filter);
}
However filter is always null. I've tried using a String rather than a map but that is also always null.
How do I go about accepting an unknown number of params in this manner? I get the feeling this is really simple but I'm just missing the obvious...
Turns out it was simple like I thought. When using a Map in the #RequestParam it takes all the incoming params, regardless of what they are.
So from what I can tell the correct solution is to do something like
#GetMapping(produces = APPLICATION_JSON)
public String getFeeds(#RequestParam MultiValueMap<String, String> params) {
params.forEach(//something);
}
I think what you are looking for is just an Array or a List, something like below :
public String getFeeds(#RequestParam(value = "filter", required = false) List<String> filters) { ... }

How to force Spring HATEOAS resources to render an empty embedded array?

I have the following controller method:
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, value = "session/{id}/exercises")
public ResponseEntity<Resources<Exercise>> exercises(#PathVariable("id") Long id) {
Optional<Session> opt = sessionRepository.findWithExercises(id);
Set<Exercise> exercises = Sets.newLinkedHashSet();
if (opt.isPresent()) {
exercises.addAll(opt.get().getExercises());
}
Link link = entityLinks.linkFor(Session.class)
.slash(id)
.slash(Constants.Rels.EXERCISES)
.withSelfRel();
return ResponseEntity.ok(new Resources<>(exercises, link));
}
So basically I am trying to get the expose a Set<> of Exercise entities for a particular Session. When the exercises entity is empty however I get a JSON representation like this:
{
"_links": {
"self": {
"href": "http://localhost:8080/api/sessions/2/exercises"
}
}
}
So basically there is no embedded entity, while something like the following would be preferrable:
{
"_links": {
"self": {
"href": "http://localhost:8080/api/sessions/2/exercises"
}
},
"_embedded": {
"exercises": []
}
}
any idea how to enforce this?
The problem here is that without additional effort there's no way to find out that the empty collection is a collection for Exercise. Spring HATEOAS has a helper class to work around this though:
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
EmbeddedWrapper wrapper = wrappers.emptyCollectionOf(Exercise.class);
Resources<Object> resources = new Resources<>(Arrays.asList(wrapper));
An EmbeddedWrapper allows you to explicitly mark objects to be added to the Resource or Resources as embedded, potentially even manually defining the rel they should be exposed under. As you can see above the helper also allows you to add an empty collection of a given type to the _embedded clause.
One can use the PagedResourceAssembler::toEmptyResource() method. For example, the following works:
Page<EWebProduct> products = elasticSearchTemplate.queryForPage(query, EWebProduct.class);
if(!products.hasContent()){
PagedResources pagedResources = pageAssembler.toEmptyResource(products, WebProductResource.class,baseLink);
return new ResponseEntity<PagedResources<WebProductResource>>(pagedResources, HttpStatus.OK);
}
I'd bet it works with other ResourceAssemblers as well.
If you have a Page< T >, you can convert it like this:
public static <T> PagedModel<EntityModel<T>> toModel(PagedResourcesAssembler<T> assembler,
Page<T> page) {
if (!page.isEmpty()) {
return assembler.toModel(page);
} else {
// toEmptyModel renders the _embedded field (with an empty array inside)
return (PagedModel<EntityModel<T>>) assembler.toEmptyModel(page, TenantSubscriptionResponseDto.class);
}
}
(You can obtain the PagedResourcesAssembler assembler by simply adding it as a parameter to the Controller method, and Spring will inject it).
Spring by default uses Jackson parser to serialize/deserialize json. As per http://wiki.fasterxml.com/JacksonFeaturesSerialization Jackson has a feature called WRITE_EMPTY_JSON_ARRAYS and its enabled by default. Maybe WRITE_EMPTY_JSON_ARRAYS is set to false in your config. please recheck your message converters configuration.

Resources