Spring MVC #RequestParam values not being extracted from URI as expected - java

I am wondering how spring split each parameters of a http request.
By example i have this method definition :
#RequestMapping(value = "/search.do", method = RequestMethod.GET)
public String searchGet(ModelMap model,
#RequestParam(value = "memberId", required = false) Integer memberId,
#RequestParam(value = "member", required = false) String member) {...}
and i use this url :
/search.do?member=T&O=
i get member = T and not member =T&O=
The request params are limited to only memberId and member.
Can i configure spring for solving this problem ?

Some characters in URLs have a special meaning. If they are supposed to be part of a value they need to be escaped.
If your value is T&O= then it needs to be changed to T%26O%3D

Looking at your controller code, your URL should have been
/search.do?memberId=T&member=
Then request parameter names will get mapped correctly.
If you wish to use same URL as mentioned in your question, change controller code to :
public String searchGet(ModelMap model,
#RequestParam(value = "O", required = false) Integer memberId,
#RequestParam(value = "member", required = false) String member) {...}

& is used to seperate request parameters.
URL contain request param name and value in following format
http://host_port_and_url?name1=value1&name2=value2&so_on
In your case
/search.do?member=T&O=
Name -> Value
member -> T
O -> (No value- Blank)
So you are getting correct values

Related

How to decouple client from entity variable names when Spring JPA sorting is used

I am using Spring JPA Pageable to manage the pagination and sorting for an endpoint. The issue is the client should use the same variable name for sorting as the entity name for JPA to be able to accept the sort request. However, I am trying to decouple the sort in the request from the entity so if the entity variable is renamed there is no impact on the request params.
#Entity
class Example {
#Id
#GeneratedValue
#Column(nullable = false)
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
// constructor, getters etc.
}
// controller
#GetMapping("/examples")
public ResponseEntity<ExampleResponseModel> getAllExamples(
#RequestParam(required = false, defaultValue = "1") int pageNum,
#RequestParam(required = false, defaultValue = "10") int pageSize,
#RequestParam(required = false, defaultValue = "firstName, lastName") String[] sortFields,
#RequestParam(required = false, defaultValue = "asc") String sortOrder {
Page<Example> pages = repository.findAll(PageRequest
.of(pageNum - 1, pageSize, Sort.by(Direction.fromString(sortOrder), sortFields)));
return new ResponseEntity<>(pages, HttpStatus.OK);
}
One way could be implementing a mapping in the middle to translate the request model sort field names to Entity field names. However, this will be very verbose and does not solve the sorting sensitivity to the field name in the Entity class. My primary objective is to decouple the client from entity entirely when it comes to sorting and also make sure that if another developer renames firstName variable of Example class to firstname the sorting won't break (or at least he gets a compile-time error to notify him to fix it).
If you use paging and sorting I would guess that you have a grid or something similar at the frontend. If so, you can create your own annotation which would keep a meta-name of the property that wouldn't be changed whatever happened.
After that you should create a new GET method in the controller which would have requested all the metas and its real names read through the reflection. Entering the grid tab or whatever, front should, firstly, check its storge looking for metas of the Example entity. If succeed, just take this data and use in the request, if not, request the new endpoint to get metas with mapped real names and store it and only then make a request with sorting and paging. If front- and back and are independent then add actualization of metas while backend startup.

semicolon in the restful service URL truncate the characters after it

I have the below code as my restful service operation.
#GET
#UnitOfWork
#Timed(name = "get-requests")
#Path("/{referenceId}")
public Response get(#Auth #ApiParam(access = "internal") UserPrincipal user,
#ApiParam(name = "id", value = "reference ID", required = true)
#PathParam("referenceId") String id) {
return Response.ok(id).build();
}
However, I noticed if I pass in m1234;5678, I get only m1234 returned. I tried #Path("/{referenceId:.*}"), but it doesn't work.
I also tried use #Encode at the top of the method to make sure the url is not decoded and then try to replace %3B with ";" in the code. But it seems not working also.
Please note that I cannot use Spring framework. Thanks.
The ; denotes a matrix parameter. Use #MatrixParam to get its value.
See also the answers to this question: URL matrix parameters vs. request parameters
Edit: The key of the matrix parameter would be 5678, the value would be null.
There is a way to get achieve what you want by using PathSegment as the type of the parameter instead of String:
#PathParam("referenceId) PathSegment id
In the body of the method, you can use
String idValue = id.getPath();
to get m1234;5678.

Spring MVC Ajax: Passing Empty Array to Ajax Controller

I have the following which works well in receiving a non-empty Array,
$.ajax({
url: "ajaxController",
dataType: "json",
type: "get",
data: {
'term': request.term,
'exclude': ["45","66"]
},
Controller (note the [] in RequestParam Value -- the result goes in a String[]):
public List<KeyValueBean> getChoices(String term,
#RequestParam(value = "exclude[]")
String[] exclude) {
}
But if I pass an empty array in the same code, which sometimes happens, it breaks:
'exclude': []
or alternatively
'exclude': JSON.stringify([])
Error:
org.springframework.web.bind.MissingServletRequestParameterException: Required
String[] parameter 'exclude[]' is not present
If you pay attention to the error, it says your request parameter exclude can not be null. If you need to can send empty array sometimes, you can mark your parameter as optional(not required) in this way:
#RequestParam(required = false, value = "exclude[]")
The problem is probably because you passed your #RequestParam with value="exclude[]" while you are passing the object named as "exclude".
So, it actually should be:
public List<KeyValueBean> getChoices(String term,
#RequestParam(value = "exclude")
String[] exclude) {
}

How to get unknown request parameter in #RequestParam SpringBoot

I am trying to implement datatable editor with spring boot ,but the client to server data varies for create ,update and delete even not constant for create as well and depends on fields
I have implemented this till now
#RequestMapping(value="/datatabledata" , method=RequestMethod.POST)
#ResponseBody
public String datatabledata(HttpServletRequest request)
{
Enumeration<String> params = request.getParameterNames();
while(params.hasMoreElements()){
String paramName = params.nextElement();
System.out.println("Parameter Name - "+paramName+", Value - "+request.getParameter(paramName));
}
//System.out.println(data);
//System.out.println(request.);
//Map<String,String>ak=new HashMap<>();
//ak.put("data", "hello ");
return "done";
}
Above code prints following output on console for create
Parameter Name - action, Value - create
Parameter Name - data[0][username], Value - dddddd
Parameter Name - data[0][date], Value - 2018-11-28
Parameter Name - data[0][balance], Value - dddddddddd
and this for edit
Parameter Name - action, Value - edit
Parameter Name - data[5bfab595507af613f409c0c4][username], Value - four
Parameter Name - data[5bfab595507af613f409c0c4][date], Value - 2018-11-25
Parameter Name - data[5bfab595507af613f409c0c4][balance], Value - 9000.0
The only constant parameter here is action and so I can use
#RequestParam("action")
but how to get rest data ?? something like #RequestParam() String data
You can create a DTO class which can be mapped from the request and can be used further.
#RequestMapping(value="/datatabledata" , method=RequestMethod.POST)
#ResponseBody
public String datatabledata(HttpServletRequest request)
{
UserDTO object = new ObjectMapper().setDateFormat(simpleDateFormat).readValue(request.getReader(), UserDTO.class);
performYourOperation(object);
}
I see we should utilize REST in more richer way here.
So create three different controller method handlling create, update and delete and maps them to difference HTTP methods like below :
//For Create. Take the parameter as (#RequestBody List<User>)
#RequestMapping(value="/datatabledata" , method=RequestMethod.POST)
//For Update/Edit, Take the parameter as (#RequestBody List<User>)
#RequestMapping(value="/datatabledata" , method=RequestMethod.PUT)
//For Delete, Just take either list of ids or id to be delete. Nothiing else required
#RequestMapping(value="/datatabledata" , method=RequestMethod.DELETE)
Now you don't need action as parameter. Client just need to specify the correct http method.
You should use #RequestParam Map<String,String> allRequestParams in your endpoint:
#RequestMapping(value="/datatabledata" , method=RequestMethod.POST)
#ResponseBody
public String datatabledata(#RequestParam Map<String,String> allRequestParams) {
/ ... rest of your code
}

Spring MVC #RequestParam -- multiple key names? or another way to require "one or the other"

What is the best way to allow multiple names for query parameters? I have a web service which has changed parameter names, but must continue for a while to accept the old names.
I'm loath to create 2 RequestParams, both not required, b/c I do require one or the other to be present. Something like this would be sweet:
#RequestParam(value = "startTime|start", required = true ) String startTime,
rather than
#RequestParam(value = "startTime", required = false ) String startTime,
#RequestParam(value = "start", required = false ) String start ){
if ( start != null || startTime != null ){ // ...
Is there any way to do this? Thanks.
You can do something like this (create a proxy method that will intercept the "old" calls and transfer them to the new method):
#RequestMapping(value="/your-mapping", params = {"oldparam1", "oldparam2", ...})
public Whatever yourOldMethod(#RequestParam(value="oldparam1", required=true) String oldParam1, ...){
return yourNewMethod(oldParam1, ...);
}
#RequestMapping(value="/your-mapping", params = {"newparam1", "newparam1", ...})
public Whatever yourNewMethod(#RequestParam(value="newparam1", required=true) String oldParam1, ...){
//do whatever you need to do here
}
When you don't need to support the old calls, simply delete yourOldMethod.
The beauty here, is using the "params" of #RequestMapping, thus allowing 2 methods to listen to the same "URL" (with different params for each)

Categories