SpringBoot accessing params inside method - spring

New to Spring/SpringBoot, I have two methods like bellow -
1.
#GetMapping("/score")
public UserScoreRes getUserScore(#RequestHeader("api-key") String apiKey,
#RequestHeader("jwt") String jwt,
#RequestParam(name = "date", defaultValue = "2022-07-15") String dateStr) {
.....
}
#GetMapping(value = "/score", params ="behaviour-version=2")
public UserScoreResV2 getUserScoreV2(#RequestHeader("api-key") String apiKey,
#RequestHeader("jwt") String jwt,
#RequestParam(name = "date", defaultValue = "2022-07-15") String dateStr) {
....
}
In method 2 (getUserScoreV2), how can I get behaviour-version inside the method?
Tried with -
#GetMapping(value = "/score", params ="behaviour-version=2")
public UserScoreResV2 getUserScoreV2(#RequestHeader("api-key") String apiKey,
#RequestHeader("jwt") String jwt,
#RequestParam(name = "behaviour-version", required = false) String behaviourVersion,
#RequestParam(name = "date", defaultValue = "2022-07-15") String dateStr) {
....
}
but, it is not working.
Can not modify method 1 (getUserScore) because a version is released in production with that.

try this
#GetMapping(value = "/score", params ="behaviour-version=2")
public UserScoreResV2 getUserScoreV2(#RequestHeader("api-key") String apiKey,
#RequestHeader("jwt") String jwt,
#RequestParam(name = "date", defaultValue = "2022-07-15") String dateStr,
ServletRequest request) {
String behaviorVersion = request.getParameter("behaviour-version");
}
warning
As Deinum mentioned, behavior-version should be 2.
Otherwise, you can't send request. The server will return 400 error

You can do this using handler interceptor. What you have to do is when application recieve a request for /score endpoint with behaviour_version header or param you have to redirect that to the another endpoint through preHandler (ex: /score/v2 ).
Then caller can send request for same endpoint with additional header/param. Redirecting according to header do by springboot interceptor.
Ex handler inteceptor:
public class ScoreInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if(request.getParameter(behaviour_v)=="2"){
response.redirect('/score/v1');
return false;
}
return true;
}
}

Related

Can spring map POST parameters by a way other than #RequestBody

I am using #RestControllers with an application where all requests are POST requests ... As I learned from this post , you can't map individual post parameters to individual method arguments, rather you need to wrap all the parameters in an object and then use this object as a method parameter annotated with #RequestBody thus
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestParam(value="idNumber") String idNumber , #RequestParam(value="applicationId") String applicationId) {
return customerService.requestOTP(idNumber, applicationId);
will not work with a POST request of body {"idNumber":"345","applicationId":"64536"}
MY issue is that I have A LOT of POST requests , each with only one or two parameters, It will be tedious to create all these objects just to receive the requests inside ... so is there any other way similar to the way where get request parameters (URL parameters) are handled ?
Yes there are two ways -
first - the way you are doing just you need to do is append these parameter with url, no need to give them in body.
url will be like - baseurl+/requestotp?idNumber=123&applicationId=123
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestParam(value="idNumber") String idNumber , #RequestParam(value="applicationId") String applicationId) {
return customerService.requestOTP(idNumber, applicationId);
second- you can use map as follows
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody Map<String,Object> body) {
return customerService.requestOTP(body.get("idNumber").toString(), body.get("applicationId").toString());
I have change your code please check it
DTO Class
public class DTO1 {
private String idNumber;
private String applicationId;
public String getIdNumber() {
return idNumber;
}
public void setIdNumber(String idNumber) {
this.idNumber = idNumber;
}
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
}
Rest Controller Method
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody DTO1 dto){
System.out.println(dto.getApplicationId()+" (------) "+dto.getIdNumber());
return "";
}
Request Type -- application/json
{"idNumber":"345","applicationId":"64536"}
OR
#RequestMapping(value="/requestotp",method = RequestMethod.POST)
public String requestOTP( #RequestBody String dto){
System.out.println(dto);
return "";
}

FeignClient: Serialize RequestParam to JSON

I have a spring boot application in which I am trying to use Feign to communicate with a remote service. My #FeignClient is defined as follows:
#FeignClient(name="TEST_SERVICE", url="URL")
#Component
public interface SomeServiceClient
{
#RequestMapping(
method = RequestMethod.POST,
value = "/someService",
consumes = "application/json",
produces = "application/json"
)
SomeServiceResult getServiceResult(
#RequestParam(value = "mode") String mode,
#RequestParam(value = "payload") SomeServicePayload payload
);
}
I would like the payload object of type SomeServicePayload to be serialized into JSON. I expected this to happen automatically, but it does not. Instead, payload is serialized to its fully qualified class name.
Do I need to set a custom encoder/decoder combination for this client? How would I do this?
#FeignClient under the hood used toString() method for bulding request string. The easiest way to create proper request is to override toString() method manually:
class SomeServicePayload{
String payload;
#Override
public String toString() {
return "{\"payload\":\"" + payload + "\"}";
}
}
Or for complex objects by using ObjectMapper:
public class SomeServicePayload{
private String payload;
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
private ObjectMapper mapper = new ObjectMapper();
#Override
public String toString() {
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
//...
}
return null;
}
}

Spring REST Service Controller not being validate by #PathVariable and #Valid

#Controller
#EnableWebMvc
#Validated
public class ChildController extends ParentController<InterfaceController> implements InterfaceController{
#Override
#RequestMapping(value = "/map/{name}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public List<Friends> getAllFriendsByName(
#Valid
#Size(max = 2, min = 1, message = "name should have between 1 and 10 characters")
#PathVariable("name") String name,
#RequestParam(value="pageSize", required=false) String pageSize,
#RequestParam(value="pageNumber", required=false) String pageNumber,
HttpServletRequest request) throws BasicException {
//Some logic over here;
return results;
}
#ExceptionHandler(value = { ConstraintViolationException.class })
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleResourceNotFoundException(ConstraintViolationException e) {
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations ) {
strBuilder.append(violation.getMessage() + "\n");
}
return strBuilder.toString();
}
Hi, I am trying to do pretty basic validation for a spring request parameter but it just doesn't seem to call the Exception handler, could someone point me into the right direction
P.S. I keep getting NoHandlerFoundException
Spring doesn't support #PathVariable to be validated using #Valid. However, you can do custom validation in your handler method or if you insist on using #Valid then write a custom editor, convert your path variable value to an object, use JSR 303 bean validation and then use #Valid on that object. That might actually work.
Edit:
Here's a third approach. You can actually trick spring to treat your path variable as a model attribute and then validate it.
1. Write a custom validator for your path variable
2. Construct a #ModelAttribute for your path variable and then use #Validator (yes not #Valid as it doesn't let you specify a validator) on that model attribute.
#Component
public class NameValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return String.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
String name = (String) target;
if(!StringUtils.isValidName(name)) {
errors.reject("name.invalid.format");
}
}
}
#RequestMapping(value = "/path/{name}", method = RequestMethod.GET)
public List<Friend> getAllFriendsByName(#ModelAttribute("name") #Validated(NameValidator.class) String name) {
// your code
return friends;
}
#ModelAttribute("name")
private String nameAsModelAttribute(#PathVariable String name) {
return name;
}

Request Body with optional property

I have an endpoint which receives a JSON through POST request.
RequestMapping(value = "/app/login", method = RequestMethod.POST,
headers = { "Content-type=application/json" })
#ResponseBody
public LoginResponse logIn(#RequestBody LoginRequest jsonRequest) {
// code
}
LoginRequest:
public class LoginRequest {
private String user;
private String password;
private String idPush;
private Integer idDevice;
// getters and setters
}
Is there anyway I can specify idDevice as optional?
If I don't send idDevice inside the json, Spring returns a 400 error.
It seems that setting the RequestBody to optional, makes any property optional, not only the full bean.
public LoginResponse logIn(#RequestBody(required=false) LoginRequest jsonRequest) {

how to check if a request param/query param is passed in the request in Spring MVC app?

I have two different requests to handle
localhost:8080/myapp/status
localhost:8080/myapp/status?v
Please note that in the second request, just a request param is passed. No value is required to be set for it. That is the requirement.
How will I handle this in my controller?
#RequestMapping(value = "/status", method = RequestMethod.GET)
#ResponseBody
public void status(
#RequestParam(value = "v", required = "false") final String verbose) {
//check if v is in query params
...logic
//else
..logic
}
You could use HttpServlerRequest.getParameterMap() like this:
#RequestMapping(value = "/status", method = RequestMethod.GET)
#ResponseBody
public void status(HttpServletRequest request) {
boolean verbose = request.getParameterMap().containsKey("v");
if (verbose) {
...
} else {
...
}
}

Resources