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

#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.

Related

Response Caching using VaryByQueryKeys with dictionary querystring argument

My ASP.NET Core MVC application has a controller method with a dictionary argument, passed via the request query string:
public async Task<IActionResult> Get([FromQuery] Dictionary<string, string> filterCriteria)
{
}
The application uses Response Caching with the VaryByQueryKeys option to return cached responses for requests that have previously been served. This works as required for simple querystring values, using an attribute on the method with the following syntax: [ResponseCache(VaryByQueryKeys = new[] { "argName" }, Duration = 20)]
However, I want to use it with a dictionary argument illustrated above.
Can it be used with an object argument like a dictionary? What is the syntax to use when specifying the list of querystring keys in the ResponseCache attribute?
It turns out you can specify queryKeys for a dictionary argument using the following syntax:
[ResponseCache(VaryByQueryKeys = new[] { "argName[key1]", "argName[key2]", ... }, Duration = 20)]

Mongo Aggregation of $where: function() using MongoTemplate in SpringBoot [duplicate]

i'm trying to compare two string in mongoDB spring Data.
My Code:
#GET
#Path("/reqvolatility")
#Produces(MediaType.APPLICATION_JSON)
public long getReqVolatility() throws JsonParseException, JsonMappingException, IOException{
String query = "{},{_id:0,levelId:1,reqID:1,creationTime:1,lastModified:1}";
Query query1 = new BasicQuery(query);
query1.addCriteria(Criteria.where("creationTime").ne("lastModified"));
long reqvolatility = getMongoOperation().count(query1,RequirmentVO.class);
return reqvolatility;
}
In the above code "creationTime" & "lastModified" columns in mongoDB.I'm comparing those two fields, but its not giving correct count.
Is this correct? if it is wrong, How can i compare two fileds?
Standard query operations do not compare the values of one field against another. In order to do this, you need to employ the JavaScript evaluation server side which can actually compare the two field values:
Assuming both fields are ISODate instances
BasicQuery query = new BasicQuery(
new BasicDBObject("$where", "this.creationTime.getTime() != this.lastModified.getTime()")
);

Spring : Adding FlashAttribute in RedirectAttributes

I am new in spring and learning RedirectAttributes.Below is th sample code I am executing.
#RequestMapping(value="/")
public String app(Model model,RedirectAttributes rm) throws IOException{
UserCO userCO= new UserCO();
userCO.setEmail("rptdbay#gmail.com");
rm.addFlashAttribute("flashkey", "flashvalue");
rm.addFlashAttribute("userCO", userCO);
return "redirect:/controller1";
}
#RequestMapping(value="/controller1")
public String app1(Model model,HttpServletRequest request) throws IOException{
System.out.println("=====================================");
System.out.println("In Controller 1");
Map md = model.asMap();
for (Object modelKey : md.keySet()) {
Object modelValue = md.get(modelKey);
System.out.println("Model data =="+ modelKey + " -- " + modelValue);
}
java.util.Enumeration<String> reqEnum = request.getParameterNames();
while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
System.out.println("Request data =="+ s+" : "+ request.getParameter(s));
}
return "redirect:/controller2";
}
#RequestMapping(value="/controller2")
public String app2(Model model,HttpServletRequest request) throws IOException{
System.out.println("=====================================");
System.out.println("In Controller 2");
Map md = model.asMap();
for (Object modelKey : md.keySet()) {
Object modelValue = md.get(modelKey);
System.out.println("Model data =="+ modelKey + " -- " + modelValue);
}
java.util.Enumeration<String> reqEnum = request.getParameterNames();
while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
System.out.println("Request data =="+ s+" : "+ request.getParameter(s));
}
return "redirect:/controller3";
}
I have added String "flashvalue" and a object of bean userCO.I observed that-
Both "flashvalue" and userCO are available in Controller1.
Fine!,but in Controller2 "flashvalue" is available but userCO
not .Why so?
In Controller1 data is coming in Model ony but in Controller2 same
data is availavble in request only.Why so?
Below is my console log.
=====================================
In Controller 1
Model data ==userCO -- com.ttnd.mvc_mod.co.UserCO#60098260
Model data ==flashkey -- flashvalue
=====================================
In Controller 2
Request data ==flashkey : flashvalue
Is there any other way possible to get FlashAttribute rather than binding in Model?
First the explanation of your console log: You receive two model objects in controller 1. This is because you specify two flash attributes in method app. Flash attributes are stored in the HTTP session for the target controller, which retrieves it from there as model attributes. After that the flash attributes are erased by Spring.
In controller 2 the flash attributes are no longer available. The reason why “flashkey” is available in controller 2 as request attribute is, since primitive types in models are exposed as query parameters to the redirect target. As mentioned before, the flash attributes passed to controller 1 are exposed as model attributes. Your “userCO” is no primitive type, so you it will not get it as request parameter, while you will get “flashkey” as query parameter in controller 2.
If you want to switch off the “forward” of model objects as request parameters, set org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(boolean) to true (as suggested by Spring for ‘new’ applications).
I don’t know whether there is another way to get flash attributes. I’d encourage you to not work around the concepts of Spring here but handle the flash attributes just like you did in your question. Think of flash attributes as model attributes in your redirect target. If you – for what reason – need two redirect until you reach your target, add the desired attributes a second time as flash attributes in controller 1.
See http://docs.spring.io/spring/docs/4.1.7.RELEASE/spring-framework-reference/html/mvc.html#mvc-ann-redirect-attributes and http://docs.spring.io/spring/docs/4.1.7.RELEASE/spring-framework-reference/html/mvc.html#mvc-flash-attributes for any details.

Sorting a custom JPA query with pageable

So, I've already done this using the standard Spring Data JPA interface which extends PagingAndSortingRepository in order to achieve pagination and sorting for a REST API. The thing is, now I want to achieve the very same thing but now using just vanilla JPA and so far so good I managed to get my API to paginate but the sorting doesn't work at all. Every time I try to set the parameter (from a pageable object using pageable.getSort()) it ends with a query error (either if I just send a string as parameter like "name" or just send the sort object, it shows errors).
Here's some code:
My repo implementation:
#Override
public List<Project> findByAll(Pageable pageable) {
Query query = em.createQuery("SELECT project FROM Project project ORDER BY :sort");
query.setParameter("sort", pageable.getSort());
query.setMaxResults(pageable.getPageSize());
query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber());
return query.getResultList();
}
My service:
#Override
public Page<Project> findAll(Pageable pageable) {
objects = Lists.newArrayList(repository.findByAll(pageable));
PageImpl<Project> pages= new PageImpl<Project>(objects, pageable, repository.count());
return pages;
}
To be clear, I'm filling the Pageable object via URI and from the console I can say it's actually getting the data, so I assume the problem is with the repo.
Edit: This is the error I get when I replace the setParameter("sort", ...) for a hardcoded string aka query.setParameter("sort", "name"):
java.lang.NumberFormatException: For input string: "name"
And I think this method should stand for strings as well. If I use query.setParameter("sort", pageable.getSort()), the error is the same.
The order by cannot be set as a query parameter. Also, the Pageable.getSort().toString() likely won't return a string suitable for use in an order by clause as it will result in a String that represents the Order as property: ORDER, note the colon.
Here are some modifications that will work, assuming Java 8...
String order = StringUtils.collectionToCommaDelimitedString(
StreamSupport.stream(sort.spliterator(), false)
.map(o -> o.getProperty() + " " + o.getDirection())
.collect(Collectors.toList()));
Query query = em.createQuery(
String.format("SELECT project FROM Project project ORDER BY %s", order));

how to create query parser to parse query param in spring REST

My query parameter is like this:
q=name:abc+age:20+roleid:(23|45)|audeince:(23|24).Here + is for AND | is for OR
I have to accept this query param as it is into my spring controller and have to make query to solar to fetch the data.

#Controller
#RequestMapping("/user")
public class BooksController {
#RequestMapping(value="/details", method=RequestMethod.GET)
public ResponseEntity<?> getUser(final HttpServletRequest request) {
String params = requestParams.get("q")[0];
//passing this string to make query in apache solar
}
}
I need to write a parser to split the param value to make a solar query.how to write a query parser to split the above url to make solar query satisfying OR AND condition.name:abc+age:20+roleid:(23|45)|audeince:(23|24) means create a solar query where name=abc and age=20 and roleid in (23,24) or audience in (23,24) .This way user sends query.
Eg:firstName:(abc|bcd)+lastName:abc+emailId:abc+dsID:abc|countryCd:US+audienceId:(123+678)
first using regex convert like this
firstName:(abc|bcd)+ -----------segment1
lastName:abc+.............segment2
emailId:abc+.............segment3
dsID:abc|--------------segment4
countryCd:US+----------segment5
audienceId:(123+678)------segment 6;
like many segments may come in the url
i have a class called
class Queryobj{
private String field;
private List value;
private String internalOperator;
private String externalOperator;
}
firstName:(abc|bcd)+ again using regex map Like this
field=firstName
value={abc,bcd}
internalOperator=|
externalOperator=+
like second segment emailId:abc+
field=emailId
value=abc
internalOperator=null
externalOperator=+
same for other segments also .if there are n segments we have n objects.
After that add each object to Linked List.if internalOperator or externalOperator is null then leave it as null .How can I achieve that
You can use this regex pattern to get "key:value operator" segment
Pattern keyValuePattern = Pattern.compile("[\\w]+:([\\w#.]+|\\([\\w|+#.]+\\))[+|]?");

Resources