Spring #RequestParameter Not a String Error - spring

I have an internal server error with #RequestParam annotation and this is the error below:
2021-11-19 19:09:46.012 ERROR 68100 --- [nio-8080-exec-3] app.gym.v1.Resource.UserControl : Required request parameter 'isNonLocked' for method parameter type String is not present
2021-11-19 19:09:46.014 WARN 68100 --- [nio-8080-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'isNonLocked' for method parameter type String is not present]
this is the code for the resource that gives the API response for that parameter:
#PutMapping("/update")
public ResponseEntity<User> update(#RequestParam("currentUsername") String currentUsername,
#RequestParam("username") String username,
#RequestParam("email") String email,
#RequestParam("role") String role,
#RequestParam("isActive") String isActive,
#RequestParam("isNonLocked") String isNonLocked) throws UserNotFoundException, UsernameExistException, EmailExistException, IOException {
User updatedUser = userService.updateUser(currentUsername, username,email, role, Boolean.parseBoolean(isNonLocked), Boolean.parseBoolean(isActive));
return new ResponseEntity<>(updatedUser, OK);
}
The problem is with the isNotLocked parameter it said it should be a string but I was parsing it correctly from boolean to string so I don't know what is the issue.

Related

org.springframework.web.HttpMediaTypeNotSupportedException: Content type '' not supported

I am getting the below error when fetching user which intern call a microservice to get album data. Album get request is working fine independently with direct IP address like -
http://{ip address}/users/8cd8b369-fb6f-40d3-9c22-78505110b8de/albums
but getting the below log in user microservice when getting a User data-
http://localhost:8082/user-ws/users/8cd8b369-fb6f-40d3-9c22-78505110b8de
enter image description here
#GetMapping(value = "/{userId}",
consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<UserResponseModel> getUser(#RequestParam String userId){
UserDto userDto = userService.getUserByUserId(userId);
UserResponseModel returnValue = new ModelMapper().map(userDto, UserResponseModel.class);
return ResponseEntity.status(HttpStatus.OK).body(returnValue);
}
User Microservice log:
2021-06-27 17:15:57.305 INFO 2580 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
2021-06-27 17:17:22.296 WARN 2580 --- [o-auto-1-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type '' not supported]
2021-06-27 17:17:32.533 WARN 2580 --- [o-auto-1-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type '' not supported]
Album microservice controller: Which user is going to fetch data from
#RestController
#RequestMapping("/users/{id}/albums")
public class AlbumsController {
#Autowired
AlbumsService albumsService;
Logger logger = LoggerFactory.getLogger(this.getClass());
#GetMapping(
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<AlbumResponseModel> userAlbums(#PathVariable String id) {
List<AlbumResponseModel> returnValue = new ArrayList<>();
List<AlbumEntity> albumsEntities = albumsService.getAlbums(id);
if(albumsEntities == null || albumsEntities.isEmpty())
{ return returnValue;
}
Type listType = new TypeToken<List<AlbumResponseModel>>(){}.getType();
returnValue = new ModelMapper().map(albumsEntities, listType);
logger.info("Returning " + returnValue.size() + " albums");
return returnValue;
}
}
##User Microservice
server.port=${PORT:0}
spring.application.name=user-ws
eureka.client.serviceUrl.defaultZone=http://localhost:8010/eureka
##Api Gateway ]
server.port=8082
spring.application.name=api-gateway
eureka.client.service-url.defaultZone=http://localhost:8010/eureka
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
#This is working fine: #
spring.cloud.gateway.routes[0].id=users-status-check
spring.cloud.gateway.routes[0].uri= lb://user-ws
#spring.cloud.gateway.routes[0].predicates[0]=Path=/users/status/check
spring.cloud.gateway.routes[0].predicates[0]=Path=/user-ws/users/status/check
spring.cloud.gateway.routes[0].predicates[1]=Method=GET
spring.cloud.gateway.routes[0].predicates[2]=Header=Authorization, Bearer (.*)
spring.cloud.gateway.routes[0].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[0].filters[1]=RewritePath=/user-ws/(?<segment>.*), /$\\{segment}
spring.cloud.gateway.routes[0].filters[2]=AuthorizationHeaderFilter
#The below route is going to be executed for http://localhost:8082/user-ws/users/8cd8b369-fb6f-40d3-9c22-78505110b8de#
spring.cloud.gateway.routes[3].id=users-ws-get-update-delete
spring.cloud.gateway.routes[3].uri= lb://user-ws
spring.cloud.gateway.routes[3].predicates[0]=Path=/user-ws/users/**
spring.cloud.gateway.routes[3].predicates[1]=Method=GET
spring.cloud.gateway.routes[3].predicates[2]=Header=Authorization, Bearer (.*)
spring.cloud.gateway.routes[3].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[3].filters[1]=RewritePath=/user-ws/(?<segment>.*), /$\\{segment}
spring.cloud.gateway.routes[3].filters[2]=AuthorizationHeaderFilter
spring.config.import=optional:configserver:http://localhost:8082

#RequestParam return null value from Postman

I am trying to make a login Restful API using Spring Boot. I am using Postman to test API. But When I am passing email and password through postman it returns null parameters. Because of that, my other functionalities are not working. Here is my code:
LoginController
#PostMapping("/login1")
#ResponseBody
public Response1 login(#RequestParam(name="email",required=false) String email, #RequestParam(name="password",required=false) String password) {
System.out.println("Email is:"+email);
System.out.println("Password is:"+password);
return lgservice.checkLogin(email, password);
}
PostMapping URL: http://localhost:8080/login1
I am sending the following data through postman:
{
"email": "Sbjain#gmail.com",
"password": "sbj123"
}
My Expected Output is this:
{
"code": 200,
"status": "Success",
"message": "Login Successfull!",
"college": [
{
"clgId": 50,
"name": "SB Jain",
"email": "Sbjain#gmail.com",
"city": "nagpur"
}
]
}
But I am getting this:
{
"code": 500,
"status": "Failed",
"message": "Please enter valid email and password",
"isSuccess": false
}
Logs
2021-05-07 17:18:48.750 INFO 11448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-05-07 17:18:48.757 INFO 11448 --- [ main] s.c.CunsultustodayWebServicesApplication : Started CunsultustodayWebServicesApplication in 4.246 seconds (JVM running for 5.143)
2021-05-07 17:18:56.665 INFO 11448 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-05-07 17:18:56.665 INFO 11448 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-05-07 17:18:56.666 INFO 11448 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
Email is:null
Password is:null
If I am doing anything wrong please guide me. Thanks!
This happens because #RequestParam stays for query parameters (#RequestParam JavaDoc). So, the correct usage of this API will be POST http://localhost:8080/login1?email=test#example.com&password=pass.
If you want to pass your parameters in request body, you need to use #RequestBody (JavaDoc) and create a POJO containing your email and password fields or use a Map (which I don't recommend doing). Here is an example
// User.java
public class User {
private String email;
private String password;
// getters, setters, constructors, etc.
}
#PostMapping("/login1")
#ResponseBody
public Response1 login(#RequestBody User user) {
System.out.println("Email is: " + user.getEmail());
System.out.println("Password is: " + user.getPassword());
return lgservice.checkLogin(user.getEmail(), user.getPassword());
}
using json request body you'll need a pojo
class LoginRequest {
public String email;
public String password;
}
and change controller to
public Response1 login(#RequestBody LoginRequest loginRequest) {
Or send the login data as form params.
https://www.baeldung.com/spring-mvc-send-json-parameters
Reason: You are sending the login details as the JSON body which can be mapped using the #RequestBody and you are using the #RequestParam in which you have to pass the details as a query param. The ideal way is to create the DTO and use the the #RequestBody
When you use the #RequestParam you have to send the details as the query parameters
URL: host:port/endpoint?param1=value1&param2=value2
URL: http://localhost:8080/login1?email=Sbjain#gmail.com&password=sbj123
#PostMapping("/login1")
#ResponseBody
public Response1 login(#RequestParam(name="email",required=false) String email, #RequestParam(name="password",required=false) String password) {
...
}
Currently, you are sending the details as the JSON body which can be mapped using the #RequestBody and you have to create the DTO to map the keys.
class LoginRequestDTO {
public String email;
public String password;
}
#PostMapping("/login1")
#ResponseBody
public Response1 login(#RequestBody LoginRequestDTO loginRequest) {
...
}
//JSON body as input
{
"email": "Sbjain#gmail.com",
"password": "sbj123"
}
Here you may know more details on the spring boot annotations

java.lang.AssertionError: Status : 404

Please Could someone help me , I cant figure out what is the problem, I'am trying to implement a test to this method but it always gives me
java.lang.AssertionError: Status
Expected :200
Actual :400
#PutMapping("/infoUtile/update/{id}")
public Map<String,Object> editActualite(#PathVariable Long id, #Valid #RequestParam ArrayList<Long> idDeleted,
#Valid #RequestParam String content, #Valid #RequestParam String description){
InformationUtile info = this.infoUtileService.getInfoUtileById(id);
info.setContent(content);
info.setDescription(description);
info.setDate(new Date());
if(idDeleted.size() != 0) {
for (int i = 0; i < idDeleted.size(); i++) {
this.mediaService.deleteMedia(idDeleted.get(i));
}
}
InformationUtile i = this.infoUtileService.addOrEditInfoUtile(info);
return getInfoUtileWeb(i);
}
and here is my test that Im trying to implement
#Test
public void update() throws Exception {
InformationUtile informationUtile = new InformationUtile();
informationUtile.setId(1);
informationUtile.setContent("oumaima");
informationUtile.setDescription("test");
Media medias = new Media();
medias.setId(1);
medias.setType("image/png");
medias.setUrl("C:\\files\\actualite\\32769\\adobexd.png");
List<Media> allMedias = new ArrayList<Media>();
allMedias.add(medias);
informationUtile.setMedias(allMedias);
User user = new User();
user.setId(1);
user.setNom("oumaima");
informationUtile.setUser(user);
ArrayList<Long> idDeleted = new ArrayList<>();
idDeleted.add(0L);
Mockito.when(informationUtileService.getInfoUtileById(Mockito.<Long>any())).thenReturn(new InformationUtile());
Mockito.when(informationUtileService.addOrEditInfoUtile(Mockito.any(InformationUtile .class))).thenReturn(informationUtile);
mockMvc.perform(put("/infoUtile/update/{id}",informationUtile.getId()).requestAttr("idDeleted",idDeleted)
.param("content",informationUtile.getContent())
.param("description",informationUtile.getDescription())
)
.andExpect(status().isOk());
verify(informationUtileService, times(1)).getInfoUtileById(informationUtile.getId());
verify(informationUtileService, times(1)).addOrEditInfoUtile(informationUtile);
verifyNoMoreInteractions(informationUtileService);
}
You are defining three request parameters at your endpoint #Valid #RequestParam ArrayList<Long> idDeleted, #Valid #RequestParam String content, #Valid #RequestParam String description which means they are query parameters after the url, e.g. http://localhost:8080/?idDeleted=1&idDeleted=2&content=Hello&description=Duke.
The HTTP 404 indicates that Spring could not find a handler for your request, meaning the client (in your case MockMvc) has a malformed URL.
In your current MockMvc request setup you are using .requestAttr() for the idDeleted request parameter.
All of them should be .param():
mockMvc
.perform(put("/infoUtile/update/{id}",informationUtile.getId())
.param("idDeleted", idDeletedOne , idDeletedTwo)
.param("content",informationUtile.getContent())
.param("description",informationUtile.getDescription())
)
PS: I guess the #Valid annotations are redundant/not needed here as you are not checking e.g. payload which has Bean Validation annotations to verify the content.
UPDATE: .param() is overloaded with .parm(String name, String... values), so you can pass your list of idDeleted with either .param("idDeleted", idDeletedOne, idDeletedTwo) or you can pass a String[] with all your Long values represented as a String

Feign - URL encode path params

This is my contract,
#RequestLine("GET /products/{id}")
#Headers({"Content-Type: application/json"})
ApiResponse getProduct(#Param("id") String productId) throws Exception;
I want to fetch the product with id = "a/b",
If I send this as a param to getProduct("a/b")
then the URL that is formed is http://api/products/a/b and I am getting a 404 instead the url should be http://api/products/a%2Fb
Is there a way around this?
A simple config did it,
#RequestLine(value = "GET /products/{id}", decodeSlash = false)
#Headers({"Content-Type: application/json"})
ApiResponse getProduct(#Param("id") String productId) throws Exception;
The path param was correctly getting encoded but the RequestTemplate was decoding the URL again (decodeSlash=true by default) before sending out the request which was causing the issue.
In my case, when code looks like this:
#GetMapping(path = "/document/{documentId}/files/{fileId}")
ResponseEntity<byte[]> getDocument(#PathVariable("documentId") String documentId, #PathVariable(value = "fileId") String fileId);
Also problem was that #PathVariable fileId could be 123/SGINED.
Setting application.property feign.client.decodeSlash=false helped.

spring mvc controller Test with Enumeration value

i'm trying to test this Method :
#RequestMapping(value="/PersonalState/{EmployeeId}", method = RequestMethod.PUT)
public #ResponseBody Object Update(#PathVariable Integer EmployeeId, #RequestParam EmployeeState empstate) throws Exception {
EmployeeService.updateEmployeeState(entityManager.find(Employee.class, EmployeeId), empstate);
return null;
}
EmplyeeState is an enumeration , the values are saved in db as integer,this is my test Code:
#Test
public void EmployeeTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.put("/PersonalState/{empstate}",EmplyeeState.PERMANENT)
.param("EmployeeId", "550"))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk());
}
I got this Errror:
Resolved Exception:
Type = org.springframework.beans.TypeMismatchException
MockHttpServletResponse:
Status = 400
I tried to pass the two variables as parameters ,passing only the EmployeeId as parameter but i still have the same error besides the param parameters must be both of type String.
Any Idea?
Problem resolved.
i passed as parameter the enum string value.

Resources