#RequestParam return null value from Postman - spring

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

Related

spring/hibernate validation -> error message is not passed to caller?

I am using org.springframework.boot:spring-boot-starter-validation:2.7.0(which in turn uses hibernate validator) to validate user input to rest controller.
I am using Spring Boot Web Starter (2.7.0) based project with #RestController annotation
My #GetMapping method is something like below -
#GetMapping(path = "/abcservice")
public Object abcService(
#RequestParam(value = "accountId", required = true) String accountId,
#Valid #RequestParam(value = "offset", required = false, defaultValue = "0") int offset,
#Valid #RequestParam(value = "limit", required = false, defaultValue = "10000") int limit
) throws Exception {
My problem is - I want the user to know about any input validation errors so they can correct and retry. But the framework is just giving 400 status code with below message.
{
"timestamp": "2022-08-03T16:10:14.554+00:00",
"status": 400,
"error": "Bad Request",
"path": "/somepath/abcservice"
}
On the server side the request is logged in warn.
2022-08-03 21:40:14.535 WARN 98866 --- [nio-8080-exec-1]
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved
[org.springframework.web.method.annotation.MethodArgumentTypeMismatchException:
Failed to convert value of type 'java.lang.String' to required type
'int'; nested exception is java.lang.NumberFormatException: For input
string: "0s"]
I want this above error message --> Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "0s" also to be passed on to user. Is there a easy configuration based way to achieve.
I think i can add a ControllerAdvice to handle this exception and include this message in the response from handler method. But this will be a couple of lines of code. Is there an even simpler way than the ControllerAdvice approach.
Similarly if the client don't pass the mandatory accountId param, the client is just getting the same 400 response as above. No details or hints to the client about what wrong they are doing or how they can fix it.. but on the server side i can see below warn log.
2022-08-03 21:59:20.195 WARN 235 --- [nio-8080-exec-3]
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved
[org.springframework.web.bind.MissingServletRequestParameterException:
Required request parameter 'accountId' for method parameter type
String is not present]
I want the client to know about this error/exception. Nothing secret here to hide (atleast in my case).
Edit - found this config -
server.error.include-message=always
Now the issue is, bad requests are sent with 500 status code, I want them to sent as 400. Then this Q is solved.
Validations made by #Valid return with 500 Status Code. Is there anyway to tell the server to return 400 response when validations fail (without using ControllerAdvice).
If you wish to test-- you can try -->
Annotate controller with #Validated.
And execute below method and you will see 500 error but would want this to be 400.
#GetMapping("/test")
public void test(#Valid #RequestParam(value = "studentId", required = false)
#Min(value=0, message="Can not be less than 0") #Max(value=200, message="Can not be above 200") Long studentId ) {
System.out.println("hit: ");
}
And hit - http://localhost:9099/test?studentId=400
The spring in-built solution without global exception handler and with minimal config is by adding the below property in the application.properties.
server.error.include-binding-errors=always
The above property can have three values:
always ----> All api's in the app will always return well defined validation error message response.
on-param ----> All api's in the app will conditionally return well defined validation error message response based on input request param field "errors"
never ---> to disable this feature.
Example Github Project Reference
Demo Test:
package com.example.demo;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#PostMapping("/test")
public void test(#Valid #RequestBody Student student) {
System.out.println("studentData: " + student);
}
}
class Student {
#NotNull(message = "firstName cannot be null")
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Override
public String toString() {
return "Student [firstName=" + firstName + ", lastName=" + lastName + "]";
}
}
Request:
{
"firstName": null,
"lastName" : "sai"
}
Response: (with HTTP response code = 400)
{
"timestamp": "2022-08-04T05:23:58.837+00:00",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotNull.student.firstName",
"NotNull.firstName",
"NotNull.java.lang.String",
"NotNull"
],
"arguments": [
{
"codes": [
"student.firstName",
"firstName"
],
"arguments": null,
"defaultMessage": "firstName",
"code": "firstName"
}
],
"defaultMessage": "firstName cannot be null",
"objectName": "student",
"field": "firstName",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotNull"
}
],
"path": "/test"
}
Use #expection handler and controller advice this help to handle your issue

Spring #RequestParameter Not a String Error

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.

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

[SWAGGER 2 UI]: Enabling multipart / form-data requests

I have a simple controller method that captures some JSON information as well as an arbitrary number of uploaded image files:
#PostMapping(value = "/products", consumes = MediaType.MULTIPART_FORM_DATA_VALUE )
public ResponseEntity<ResponseMessage> postProduct(#RequestPart(name = "request") final MyJsonBody postRequest,
#RequestPart(name = "images") final ist<MultipartFile> images)
{
log.info("Request id and name fields: " + postRequest.getProductId() + ", " + postRequest.getProductName() + ".");
log.info("Received a total of: " + images.size() + " files.");
return success("Just wanted to test the upload functionality!", null, HttpStatus.OK); // A private method I have to return an appropriate ResponseEntity<> to the user.
}
The MyJsonBody class is a simple POJO for testing purposes:
#Data
#Builder(access = PUBLIC)
#AllArgsConstructor
#NoArgsConstructor
public class MyJsonBody
{
#JsonProperty("id") private String productId;
#JsonProperty("name") private String productName;
}
When using Postman, my example multipart/form-data POST request works just fine:
As you can see here, Springboot is completely fine with the request and prints the data as expected:
2020-12-21 14:28:20.321 INFO 11176 --- [nio-8080-exec-3] c.j.r.fileuploads.FileUploadController : Request id and name fields: RANDOM_ID, null.
2020-12-21 14:28:20.321 INFO 11176 --- [nio-8080-exec-3] c.j.r.fileuploads.FileUploadController : Received a total of: 2 files.
My team uses Swagger 2, and it's important to me to be able to debug the API using the Swagger UI. Unfortunately, when sending an identical request from the UI:
The UI appears to not be sending the request as a multipart/form-data request, but as an application/octet-stream request:
2020-12-21 14:27:40.095 WARN 11176 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported]
Most information on the Web concerns the OpenAPI spec version 3, yet we are using version 2.8 for now.
Any ideas about how I might be malforming the request?
Edit: The following is my configuration class for Swagger. All of my code in this toy projects is under package com.jason.rest.fileuploads.
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select().apis(RequestHandlerSelectors.basePackage("com.jason.rest.fileuploads"))
.paths(PathSelectors.any())
.build();
}
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
#Bean
public LinkDiscoverers discoverers() {
List<LinkDiscoverer> plugins = new ArrayList<>();
plugins.add(new CollectionJsonLinkDiscoverer());
return new LinkDiscoverers(SimplePluginRegistry.create(plugins));
}
}

NullpointerException and internal server error returned for Rest calls in Spring boot

I posted this question yesterday, and that was solved. It's now seeing the values from the Rest client; however, it always return 500 internal server error and NullPointerException for all the calls I have made. What can I do to get past this?
Error
web - 2019-09-29 14:36:11,814 [http-nio-8081-exec-1] DEBUG o.b.w.c.RegistrationController - Registering user account with information: UserDto [firstName=Kehinde, lastName=Adeoya, username=ken4ward, password=o201115#...Adel, matchingPassword=o201115#...Adel, email=kadeoya#oltega.com, isUsing2FA=false, role=ROLE_ADMIN]
web - 2019-09-29 14:36:11,823 [http-nio-8081-exec-1] ERROR o.b.w.c.e.RestResponseEntityExceptionHandler - 500 Status Code
j.l.NullPointerException: null
at o.b.s.UserService.save(UserService.java:81)
at o.b.w.c.RegistrationController.registerUserAccount(RegistrationController.java:86)
at j.i.r.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java)
at j.i.r.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at j.i.r.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
... 75 frames truncated
This is what I sent from the Rest client:
{
"firstName": "Kehinde",
"lastName": "Adeoya",
"username": "ken4ward",
"email": "kadeoya#oltega.com",
"password": "o201115#Adel",
"matchingPassword": "o201115#Adel",
"statusName": "ROLE_ADMIN"
}
This is the service layer at which it returns violation error (UserService)
public User save(UserDto user) {
Set<ConstraintViolation<UserDto>> violations = validator.validate(user);
if (violations.size() > 0) {
throw new BadRequestException();
}
............
This is the controller
#RequestMapping(value = "/registration", method = RequestMethod.POST )
#ResponseBody
public User registerUserAccount(final UserDto accountDto, final HttpServletRequest request) {
final User registered = userInterface.save(accountDto);
eventPublisher.publishEvent(new OnRegistrationCompleteEvent(registered, request.getLocale(), getAppUrl(request)));
return registered;
}

Resources