I am trying to send a post request to add an User object to the list of users (no database).
getUser, getAllUsers Api endpoints are working fine and giving desired results.
When I send a Post request with User, it is always giving 500 Internal Server Error.
I have also printed out the User and data is fine.
Below is my code -
User Entity (User.java) -
public class User {
private Integer userId;
private String name;
private String email;
private Account account;
Getters and Setters along with constructors are also there.
My User Service and its implementation -
UserService.java -
#Service
public interface UserService {
public User getUser(Integer userId);
public List<User> getAllUsers();
<T> ResponseEntity<T> deleteUser(Integer userId);
<T> ResponseEntity<User> addUser(User user);
}
UserServiceImpl.java
#Service
public class UserServiceImpl implements UserService{
List<User> userList = List.of(
new User(1311, "Vaibhav Shrivastava", "innomightmail#gmail.com"),
new User(1312, "Varun Shrivastava", "varun#gmail.com"),
new User(1313, "Rajesh Shrivastava", "rajesh#gmail.com")
);
#Override
public User getUser(Integer id) {
// TODO Auto-generated method stub
System.out.println(this.userList.stream().filter(user -> user.getUserId().equals(id)).findAny().orElse(null));
return this.userList.stream().filter(user -> user.getUserId().equals(id)).findAny().orElse(null);
}
#Override
public List<User> getAllUsers() {
// TODO Auto-generated method stub
return userList;
}
#Override
public <T> ResponseEntity<User> addUser(User user) {
// TODO Auto-generated method stub
userList.add(user);
return new ResponseEntity<User>((User)user, HttpStatus.OK);
}
Controller (UserController.java) -
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private RestTemplate restTemplate;
#GetMapping("/{userId}")
public User getUser(#PathVariable("userId") Integer userId) {
User user = this.userService.getUser(userId);
Account account = this.restTemplate.getForObject("http://account-service/account/user/" + user.getUserId(), Account.class);
user.setAccount(account);
return user;
}
#GetMapping("/all-users")
public List<User> getAllUsers(){
return this.userService.getAllUsers();
}
#PostMapping("/add-user")
public ResponseEntity<Object> addUser(#RequestBody User user) {
try {
User newUser = new User(user.getUserId(), user.getName(), user.getEmail());
this.userService.addUser(newUser);
return new ResponseEntity<>(user, HttpStatus.CREATED);
} catch (Exception e) {
User newUser = new User(user.getUserId(), user.getName(), user.getEmail());
return new ResponseEntity<>(newUser, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
getUser and getAllUsers are working fine (dont get confused by account in getUser that is my another microservice)
When I am sending Post request using postman -
{
"userId": 1314,
"name": "James",
"email": "james#gmail.com"
}
I am getting response -
500 Internal server error and User object which I wanted to check so I printed it out -
{
"userId": 1314,
"name": "Kavita Shrivastava",
"email": "kavita#gmail.com",
"account": null
}
I do not know why I am getting Internal server error.
The request should add the new User to the list in UserServiceImpl.
Stack trace has all the information you are looking for. UserServiceImple.addUser tries to add an object to the userList . but userList is an immutable list. so it throws exception. code flows to "catch" block and it returns 500 with newly created object.
In my opinion That's happens because
when U use List.of it equals to
Collections.unmodifiableList so it will throw an exception as Its Immutable.
Related
This is my Controller class and I wrote exceptions to check pin and username(because I want to make them unique) But When I enter data in Postman It gives only one of them( I want to create scenario that both pin and username are not unique and used for once) How can I write Both of them same time. I actually want to add some elements additionally like that so I'm stuck there)
I want output be like:
Ex: This pin Already in use!
This username Already in use!
#RestController
#RequestMapping("/api")
public class EmployeeController {
//private EmployeeService employeeService;
private final EmployeeRepository employeeRepository;
// ControllerException controllerException;
#Autowired
public EmployeeController(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
#PostMapping("/employee")
public ResponseEntity<Employee> saveEmployee(#RequestBody Employee employee, BindingResult bindingResult) throws EmployeeAlreadyExistsException {
if (employeeRepository.existsById(employee.getId())) {
throw new EmployeeAlreadyExistsException();
}
String temUsername = employee.getUsername();
if(temUsername !=null && !"".equals(temUsername)) {
Employee userObject = employeeRepository.findByUsername(temUsername);
if(userObject!=null) {
throw new EmployeeUsernameAlreadyExistsException();
}
}
String pin = employee.getPin();
if(pin !=null && !"".equals(pin)) {
Employee userObject = employeeRepository.findByPin(pin);
if(userObject!=null) {
throw new EmployeePinAlreadyExistsException();
}
}
Employee employee1 = employeeRepository.save(employee);
return new ResponseEntity<>(employee1, HttpStatus.CREATED);
}
#GetMapping("/employees")
public ResponseEntity<List<Employee>> getAllEmployee() throws EmployeeNotFoundException {
return new ResponseEntity<>((List<Employee>) employeeRepository.findAll(), HttpStatus.OK);
}
#PostMapping("/update")
public ResponseEntity<?> updateEmployee(#RequestBody Employee employee) throws EmployeeNotFoundException, EmployeePinAlreadyExistsException {
if (!employeeRepository.existsById(employee.getId())) {
throw new EmployeeNotFoundException();
} else {
Employee employee1 = employeeRepository.save(employee);
return new ResponseEntity<>(employee1, HttpStatus.CREATED);
}
}
#GetMapping("employee/{id}")
public ResponseEntity<?> getEmployeeById(#PathVariable("id") int id, Employee employee) throws EmployeeNotFoundException {
if (!employeeRepository.existsById(employee.getId())) {
throw new EmployeeNotFoundException();
}
return new ResponseEntity<>(employeeRepository.findById(id), HttpStatus.OK);
}
#ExceptionHandler(value = EmployeeAlreadyExistsException.class)
public ResponseEntity<?> EmployeeAlreadyExistsException(EmployeeAlreadyExistsException employeeAlreadyExistsException) {
ErrorResponse erResp = ErrorResponse.builder()
.message("This Employee already exist!")
.code("101")
.traceId(UUID.randomUUID().toString())
.build();
return new ResponseEntity<>(erResp, HttpStatus.CONFLICT);
}
#ExceptionHandler(value = EmployeeNotFoundException.class)
public ResponseEntity<?> EmployeeNotFoundException(EmployeeNotFoundException employeeNotFoundException) {
ErrorResponse erResp = ErrorResponse.builder()
.message("This id is not valid!")
.code("404")
.traceId(UUID.randomUUID().toString())
.build();
return new ResponseEntity<>(erResp, HttpStatus.CONFLICT);
}
#ExceptionHandler(value = EmployeePinAlreadyExistsException.class)
public ResponseEntity<?> EmployeePinAlreadyExistsException(EmployeePinAlreadyExistsException employeePinAlreadyExistsException) {
ErrorResponse erResp = ErrorResponse.builder()
.message("This pin Already in use!")
.code("101")
.traceId(UUID.randomUUID().toString())
.build();
return new ResponseEntity<>(erResp, HttpStatus.CONFLICT);
}
#ExceptionHandler(value = EmployeeUsernameAlreadyExistsException.class)
public ResponseEntity<?> EmployeeUsernameAlreadyExistsException(EmployeeUsernameAlreadyExistsException employeeUsernameAlreadyExistsException) {
ErrorResponse erResp = ErrorResponse.builder()
.message("This Username Already in use!")
.code("109")
.traceId(UUID.randomUUID().toString())
.build();
return new ResponseEntity<>(erResp, HttpStatus.CONFLICT);
}
}
You should define a class with attribute like private List<String> errMsg to handle response
Then, check pin and username and save check result to boolean variable. If result are false, add an element to errMsg like "pin already in use", "username already in use".
Finally, dependent to check results, if all of them are false, you can throw PinAndUsernameAlreadyExistsException with errMsg have 2 messages were added
If you need all the fields in the exception message don't throw the exception until all validations have been made, however for a public accessed application is not a good idea from a security perspective, It's better a generic error message like: "Some of your employee data is already used", anyway, if you need it for some kind of customer requirement or simple formation exercise, this code should do what you ask for:
private void assertEmployeeDataisUnique(Employee employee) throws Exception {
List<String> existingFields = new ArrayList<>();
if (employeeRepository.existsById(employee.getId())) {
existingFields.add("id");
}
String temUsername = employee.getUsername();
if(temUsername !=null && !"".equals(temUsername)) {
Employee userObject = employeeRepository.findByUsername(temUsername);
if(userObject!=null) {
existingFields.add("username");
}
}
String pin = employee.getPin();
if(pin !=null && !"".equals(pin)) {
Employee userObject = employeeRepository.findByPin(pin);
if(userObject!=null) {
existingFields.add("pin");
}
}
if(!existingFields.isEmpty()) {
throw new Exception("Employee data is not unique. Fields: " + existingFields);
}
}
#PostMapping("/employee")
public ResponseEntity<Employee> saveEmployee(#RequestBody Employee employee, BindingResult bindingResult) throws EmployeeAlreadyExistsException {
this.assertEmployeeDataisUnique(employee);
Employee employee1 = employeeRepository.save(employee);
return new ResponseEntity<>(employee1, HttpStatus.CREATED);
}
I have a problem when I try to register in spring security from the postman and it keeps giving me this error:
"timeStamp": "03-25-2021 07:21:15",
"httpStatusCode": 500,
"httpStatus": "INTERNAL_SERVER_ERROR",
"reason": "INTERNAL SERVER ERROR",
"message": "An error occurred while processing the request"
*the code for this problem as you can see below I searched a lot but no answer at all about it so I put it here:
this is the Service Code I called it (UserImplService)
public User register(String firstName, String lastName, String username, String email)
throws UserNotFoundException, UsernameExistException, EmailExistException {
validateNewUsernameAndEmail(EMPTY, username, email);
User user = new User();
user.setUserId(generateUserId());
String password = generatePassword();
user.setFirstName(firstName);
user.setLastName(lastName);
user.setUsername(username);
user.setEmail(email);
user.setJoinDate(new Date());
user.setPassword(encodePassword(password));
user.setActive(true);
user.setNotLocked(true);
user.setRole(ROLE_USER_AUTHORITIES.name());
user.setAuthorities(ROLE_USER_AUTHORITIES.getAuthorities());
user.setProfileImageUrl(getTemporaryProfileImageUrl(username));`enter code `
userRepository.insert(user);
LOGGER.info("New user password: " + password);
and this is the Controller for this service as you can see it:
even when I run the debug mode it didn’t give me a clear explanation about the bug.
#RestController
#RequestMapping(path = { "/user"})
public class UserController extends ExceptionHandling {
private UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#PostMapping("/register")
public ResponseEntity<User> register(#RequestBody User user) throws UserNotFoundException, UsernameExistException, EmailExistException {
User newUser = userService.register(user.getFirstName(), user.getLastName(), user.getUsername(), user.getEmail());
return new ResponseEntity<>(newUser, OK);
}
}
Here is my Authentication filter :
public class AuthenticationFilter implements ContainerRequestFilter {
private static Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class);
#Autowired
private AuthenticationService service;
#Override
public void filter(ContainerRequestContext context) throws IOException {
String token = context.getHeaderString("mytoken");
if (token == null || token.isEmpty()) {
String message = "token.null_empty";
logger.warn("{}: Token is required to access to personal data (resource {}) => reject request", message, context.getUriInfo().getBaseUri());
throw new AuthenticationException(ErU2, message);
}
// check token and retrieve user information
logger.debug("Checking validity of token {}", token);
IUser user = this.service.getUser(token);
logger.warn("======= AuthenticationFilter#filter token={}, user={}", token, user);
// set user data as request property
context.setProperty("user", user);
logger.warn("======= AuthenticationFilter#filter token={}, user#context.setProperty={}", token, (IUser) context.getProperty("user"));//In this line ihave user not null
}
}
#Path("perso")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
#Component
public class PersonalSpace {
private static Logger logger LoggerFactory.getLogger(PersonalSpace.class);
#Context
private ContainerRequestContext context;
private IUser getUser() {
logger.debug("[IN] PersonalSpace#getUser");
IUser user = (IUser) this.context.getProperty("user");//This returns null
logger.warn("======= PersonalSpace#getUser user={}", user);
return user;
}
#GET
#Path("/skanso/data")
public Response getData(#QueryParam("start") Integer start, #QueryParam("number") Integer number,
List<Integer> ids) {
return dataOperator.getDataLimitedList(getUser(), start, number, ids);
}
}
This code works nicely when i test with single request , but on concurrent request this.context.getProperty("user") returns null.
Is there à solution threadSafe or something like that ?
Please save my hair :)
I'm trying to use #DELETE request after a made some simple web application which I've tested using soapui. With this application I can add and get users/book to database. Now I'm trying to made a #DELETE request but I can't make it. Here is the code:
//UserServiceImpl
#PersistenceContext
private EntityManager em;
#Override
public void deleteUser(Long id) {
if (null == id || id.longValue() < 1) {
throw new IllegalArgumentException(" User id can not be null or less than zero. ");
}
User u = em.find(User.class, id);
em.remove(u);
}
//UserResource
#Autowired
private UserService userService;
#DELETE
#Path("/delete/{id}")
public Response deleteUser(#PathParam("id") String id) {
Response response;
try {
User user = userService.deleteUser(Long.valueOf(id));//here is the error
if (user != null) {
response = Response.status(HttpServletResponse.SC_OK).entity(user).build();
} else {
response = Response.status(HttpServletResponse.SC_NOT_FOUND).build();
}
} catch (IllegalArgumentException e) {
response = Response.status(HttpServletResponse.SC_NOT_FOUND).build();
}
return response;
}
I`ve fix my problem. The delete method which is in UserServiceImpl must not be void.... it must be public User deleteUser(Long id). The other delete method in Resource class ... just need to be of void type. There i do not use Response and i simply print the result like this:
System.out.print(Response.status(HttpServletResponse.SC_OK).entity(user).build());
My Question is How can I validate request parameters if I use #PathParam.
For instance I have two request parameters, name and id
path is localhost:/.../search/namevalue/idvalue
if a user submits blanks for name or id I should send a response mentioning that name is required/ id is required.
I could do the validations if I use #QueryParam, but I'm not sure how to do it if I have to use pathvariables.
If I just test using http:/localhost:/.../search/namevalue orhttp:/localhost:/.../search/idvalue or http:/localhost:/.../search/ it's throwing servlet exception.
Below is the code, if i use QueryParams validations work just fine, Please let me know the approach when i use pathparam
#Controller
#Path("/customer")
public class CustomerController extends BaseController implements Customer {
#Override
#GET
#Produces({ "application/json", "application/xml" })
#Path("/search/{name}/{id}/")
public Response searchCustomerDetails(
#PathParam("name") String name,
#PathParam("id") Integer id) {
ResponseBuilder response = null;
CustomerValidations validations = (CustomerValidations) getAppContext()
.getBean(CustomerValidations.class);
CustomerResponse customerResponse = new CustomerResponse();
CustomerService customerService = (CustomerService) getAppContext()
.getBean(CustomerService.class);
try {
validations.searchCustomerDetailsValidation(
name, id,customerResponse);
if (customerResponse.getErrors().size() == 0) {
CustomerDetails details = customerService
.searchCustomerDetailsService(name, id);
if (details == null) {
response = Response.status(Response.Status.NO_CONTENT);
} else {
customerResponse.setCustomerDetails(details);
response = Response.status(Response.Status.OK).entity(
customerResponse);
}
} else {
response = Response.status(Response.Status.BAD_REQUEST).entity(
customerResponse);
}
}
catch (Exception e) {
LOGGER.error(e.getMessage());
response = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
}
return response.build();
} }
#Component
#Scope("prototype")
public class CustomerValidations {
public void searchCustomerDetailsValidation(
String name, Integer id,
CustomerResponse customerResponse) {
if (id == null) {
customerResponse.getErrors().add(
new ValidationError("BAD_REQUEST",
""invalid id));
}
if (name== null
|| (name!= null && name
.trim().length() == 0)) {
customerResponse.getErrors().add(
new ValidationError("BAD_REQUEST", "invalid id"));
}
} }
#XmlRootElement
public class CustomerResponse {
private CustomerDetails customerDetails;
private List<ValidationError> errors = new ArrayList<ValidationError>();
//setters and getters }
public class ValidationError {
private String status;
private String message;
public ValidationError() {
}
public ValidationError(String status, String message) {
super();
this.status = status;
this.message = message;
}
//setters and getters }
You're receiving an exception because you have no methods mapped to #Path("/search/{foo}/") or #Path("/search/"), so you should be getting a default 404 response as these paths are not really defined.
I'm not sure why you would want to validate these "missing" request paths though - it looks like this endpoint is intended to be used as a query endpoint so I'd suggest you use #RequestParam/query parameters to more RESTfully describe the search you're attempting. A path of search/{name}/{id} would suggest a specific resource which permanently lives at this URL, though in this case you're querying for customers on this controller.
I would propose you drop the /search path completely and just map query parameters onto the "root" of the Customer controller, so you get something like
#Controller
#Path("/customer")
public class CustomerController extends BaseController implements Customer {
#GET
#Produces({"application/json", "application/xml"})
public Response searchCustomerDetails(
#RequestParam("name") String name,
#RequestParam("id") Integer id) {
// Returns response with list of links to /customer/{id} (below)
}
#GET
#Produces({"application/json", "application/xml"})
#Path("/{id}")
public Response getCustomerDetails(#PathVariable("id") String id) {
// GET for specific Customer
}
}