cannot perform edit operation in spring boot - java

I want to edit my User Class while passing the id and while returning user object to controller it is getting error such as "There was an unexpected error (type=Internal Server Error, status=500)".It is telling me to typecast to Optional.I don't know what to do.
UserService Class
public User editMyUser(int id) {
return userRepository.findById(id);
}
Controller Class
#RequestMapping("/edit-user")
public String editUser(#RequestParam int id, HttpServletRequest request) {
userService.deleteMyUser(id);
request.setAttribute("user", userService.editMyUser(id));
request.setAttribute("mode", "MODE_UPDATE");
return "welcome";
}

This is how findById looks like in the new version of Spring (according to docs):
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
Optional<T> findById(ID primaryKey);
// .... other methods ...
}
So, the first thing I would change in your code is :
public Optional<User> editMyUser(int id) {
return userRepository.findById(id);
}
Make your method return Optional<User>, maybe this will help.
Also, be careful when using user returned by that new method, e.g. here
request.setAttribute("user", userService.editMyUser(id));
With Optional you need to use get() to obtain the actual user instance:
userService.editMyUser(id).get()
but first, you should check if that Optional actually contains the user:
Optional<User> optionalUser = userService.editMyUser(id);
if (optionalUser.isPresent()) {
User user = optionalUser.get();
// do whatever you need
} else {
// means user is null - nothing found for ID
// act accordingly
}
There is also a good documentation that Spring provides. Could be useful.
Happy Coding :)

I let u here the cofiguration of the interface for a rest service that is working rihg now
#Api(value = "empleados", description = "the empleados API")
public interface EmpleadosApi {
#ApiOperation(value = "Buscar empleados", notes = "", response = ResultadoBusquedaEmpleado.class, tags = {})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Búsqueda de empleados", response = ResultadoBusquedaEmpleado.class) })
#RequestMapping(value = "/empleados", method = RequestMethod.GET)
ResponseEntity<ResultadoBusquedaEmpleado> empleadosGet(
#ApiParam(value = "Nombre de empleado") #RequestParam(value = "nombre", required = false) String nombre)
}

Related

How to handle Ambiguous handler methods mapped in REST application with Spring

In my UserController, I have 3 #GetMapping methods:
getAllUsers() - It is used to get all users,
getUserById() - It is used to get specific user with his unique id and
getUserByName() - It is used to get specific user with his unique name.
The code from the method is as follows:
#RestController
#RequestMapping("/users")
public class UserController {
private final UserRepository userRepository;
#Autowired
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
#GetMapping
public List<User> getAllUsers() {
return userRepository.findAll();
}
#GetMapping(value = "/{id}")
public ResponseEntity<User> getUserById(#PathVariable Long id) {
User user = userRepository.findById(id);
return ResponseEntity.ok(user);
}
#GetMapping(value = "/{name}")
public ResponseEntity<User> getUserByName(#PathVariable String name) {
User user = userRepository.findUserByName(name);
return ResponseEntity.ok(user);
}
The first problem is that my app doesn't know if the URL is String or Integer, so I solved the problem between the getUserById() and getUserByName() methods like this:
#GetMapping(value = "{id}")
public ResponseEntity<User> getUserById(#PathVariable Long id) {
User user = userRepository.findById(id);
return ResponseEntity.ok(user);
}
#GetMapping
public ResponseEntity<User> getUserByName(#RequestParam(value = "name") String name) {
User user = userRepository.findUserByName(name);
return ResponseEntity.ok(user);
}
So now I can access the methods with:
http://localhost:8080/users/1 and
http://localhost:8080/users?name=john
When I run the application, I get this error:
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'userController' method
com.db.userapp.controller.UserController#getUserByName(String)
to {GET [/users]}: There is already 'userController' bean method
I believe this is because my getAllUsers() and getUserByName() methods are on the same URL format. Does anyone have an idea how I can solve this?
You are right because both getAllUsers() and getUserByName() are mapped to the /users/. So for the request /users/ , it does not know which method should be used to process it.
You can configure the name query parameter for /users/ as optional and check if its value is null. Null means user does not want have any filtering on the users and want to get all users :
#RestController
#RequestMapping("/users")
public class UserController {
#GetMapping
public List<User> getUser(#RequestParam(value = "name",required = false) String name) {
if (Strings.isNullOrEmpty(name)) {
return userRepository.findAll();
} else {
return userRepository.findUserByName(name);
}
}
}
You're right. You have defined two GET Methods on /users and therefore its basically the same as your first problem.
You can merge these methods and set the RequestParam for name as not required and just use it when it's not null.
The problem here is that you would return one User as List even though you know that there is only one User for this name. Which I think is fine because its not the Users main identifier and with the RequestParam it's more like a filter anyway.

Spring return unauthorized when it is not data of user

I have a endpoint, where the user sees the info of hic user account. But an user should only be able to see his own data and not of another user account. So my function looks like this
#GetMapping("{userId}")
#Operation(security = #SecurityRequirement(name = OpenApiConfig.USER_AUTH))
public GetUserResponse getUser(#PathVariable UUID userId, Principal principal) {
AppUser user = getUserService.findByUsername(principal.getName()).get();
if(!user.getId().equals(userId)){
//return ResponseEntity unauthorized
}
return GetUserResponse.of(getUserService.getUser(userId).orElseThrow());
}
But as you can see I return a GetUserResponse, how can I return something like a ResponseEntity with unauthorized return code?
My GetUserResponse class looke like this
public class GetUserResponse {
private UUID id;
private String name;
public static GetUserResponse of(AppUser user) {
return new GetUserResponse(user.getId(), user.getUsername());
}
public GetUserResponse(UUID id, String name) {
this.id = id;
this.name = name;
}
As it is written now I would simply remove the parameter and use the id from the principle thus making it impossible to request information about somebody else.
If you would like to be able to let some users (admin, super etc) be able to use the same method then you could create your own exception and use the ResponseStatus annotation on that. Then simply throw that exception if the user id don't match up and the user isn't authorized to read other users.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
#ResponseStatus(code = HttpStatus.UNAUTHORIZED, reason = "Invalid target id")
public class InvalidTarget extends RuntimeException {}
Then just throw that exception if the user isn't allowed to lookup others.
You can find more information about exceptions and spring in this blog entry:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
If you want to throw an exception and return a generic response using ResponseEntity, you can use #ControllerAdvice annotation.
#ControllerAdvice annotation allows you to handle the exceptions in the whole application. It is an annotation-driven interceptor. Refer to this link to know more about exception handling and #ControllerAdvice annotation.
Refer to the below code to get more understanding about #ControllerAdvice.
Controller where the exception is thrown
#GetMapping("{userId}")
#Operation(security = #SecurityRequirement(name = OpenApiConfig.USER_AUTH))
public GetUserResponse getUser(#PathVariable UUID userId, Principal principal) {
AppUser user = getUserService.findByUsername(principal.getName()).get();
if(!user.getId().equals(userId)){
throw new UnauthorisedException("Unauthorised User");
}
return GetUserResponse.of(getUserService.getUser(userId).orElseThrow());
}
UnauthorisedException.java
#ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class UnauthorisedException extends RuntimeException {
public UnauthorisedException(String message) {
super(message);
}
}
Class with #ControllerAdvice annotation
#ControllerAdvice
public class ControllerAdviceExceptionHandler {
#ExceptionHandler(UnauthorizedException.class)
protected ResponseEntity<?> unauthorisedException(final UnauthorizedException exception) {
return new ResponseEntity<>(exception.getMessage(), HttpStatus.UNAUTHORIZED);
}

Spring REST controller with different functions

I have a Spring #RestController for manipulating my Users and I want to have several functions:
/users : GET (returns all users)
/users/:id : GET (returns a user with given ID, default id=1)
/users : POST (inserts a user)
/users/:id : DELETE (deletes a user with given ID)
I started working on it but I'm not sure how to manage the "overlapping" URIs for the same method (e.g. first two cases). Here's what I came with so far:
#RestController
public class UserController {
#RequestMapping(value = "/users", method = RequestMethod.GET)
public List<User> getAllUsers() {
return UserDAO.getAll();
}
#RequestMapping(value = "/users", method = RequestMethod.GET)
public User getUser(#RequestParam(value = "id", defaultValue = "1") int id) {
return UserDAO.getById(id);
}
}
This won't work due to "ambiguous mapping" and it's pretty clear to me, but I don't know what to do. Should I change one of the URIs or there is some other way?
Edit:
I've also tried changing the second method to:
#RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public User getUser(#PathVariable("id") int id) {
return UserDAO.getById(id);
}
Still doesn't work.
Your current mapping:
#RequestMapping(value = "/users", method = RequestMethod.GET)
public User getUser(#RequestParam(defaultValue = "1") int id)
Would map to the /users?id=42 not the desired /users/42. If you want to create a mapping for /users/:id endpoint, use the following:
#RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public User getUser(#PathVariable int id) {
return UserDAO.getById(id);
}
Also, as of Spring Framework 4.3, you can use new meta annotations to handle GET, POST, etc. methods:
#RestController
#RequestMapping("/users")
public class UserController {
#GetMapping
public List<User> getAllUsers() {
return UserDAO.getAll();
}
#GetMapping("{id}")
public User getUser(#PathVariable int id) {
return UserDAO.getById(id);
}
}

no suitable HttpMessageConverter found for response type [class com.avada.rest.UsersController$Users]

I am getting the following exception and not sure why...
Exception in thread "main"
org.springframework.web.client.RestClientException: Could not extract
response: no suitable HttpMessageConverter found for response type
[class com.avada.rest.UsersController$Users] and content type
[application/json;charset=UTF-8] at
org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:576)
at
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:529)
at
org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:236)
at com.avada.rest.UsersTest.main(UsersTest.java:18)
This is my RestController:
#RestController
#RequestMapping("/users")
public class UsersController {
#RequestMapping(method = RequestMethod.GET)
public Users getUsers() {
Users users = new Users();
users.setUsers(ConstantsHome.userprofileMgr.getUsers(null));
return users;
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User getUser(#PathVariable String id) {
return ConstantsHome.userprofileMgr.getUserByUserId(id, true, true);
}
public static class Users {
private List<User> users = new ArrayList<>();
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
}
This is my Test class:
public class UsersTest {
private static RestTemplate template = new RestTemplate();
public static void main (String[] args) throws Exception {
// Get all users
String uri = "http://localhost:8080/IR360/rest/users";
UsersController.Users users = template.getForObject(uri, UsersController.Users.class);
System.out.println("Looping through users...");
for (User user : users.getUsers()) {
System.out.println("Name=" + user.getName());
}
// Get 1 user
uri = "http://localhost:8080/IR360/rest/users/admin";
User user = template.getForObject(uri, User.class);
System.out.println("Name for single user=" + user.getName());
}
}
I can get a single user no problem if I comment out the test code for "Get all users".
What am I doing wrong in this code?
P.S. - I can make a call to getUsers() through the browser and the json comes back fine so I know getUsers() is working...just can't get the RestTemplate to work
Turned out to be an issue in my Users class (more specifically the User class in List<User>).
I updated the User class with #JsonIgnore on fields that I thought might be causing the Exception and I was able to get passed this issue.
So for others that might encounter this issue, check the object you're trying to do a getForObject on to make sure everything can map fine.

Spring Rest Controller, Path Variables on an overriden method's arguement

I have a controller annotated with #RestController and it implements an interface:
public interface ContratEndpoint {
String ROOT = "/api/contrats";
String GET_CONTRAT = "";
String GET_CONTRAT_PER_PK = "/{idContrat}";
#RequestMapping(value = GET_CONTRAT)
Contrat getContrat(#RequestParam(value = "contratId")Long contratId);
#RequestMapping(value = GET_CONTRAT_PER_ID)
ExtContrat getContratById(#PathVariable("idContrat") Long idContrat);
}
The controller:
#RestController
#RequestMapping(value = ContratEndpoint.ROOT)
public class ContratController implements ContratEndpoint {
//Injecting Services....
#Resource
private Mapper mapper;
#Override
public Contrat getContrat(Long contratId) {
return mapper.map(contratService.get(contratId),Contrat.class);
}
#Override
public ExtContrat getContratById(#PathVariable("idContrat") Long idContrat){
Preconditions.checkArgument(idContrat !=null);
return mapper.map(contratService.get(idContrat),ExtContrat.class);
}
.The above Code works just fine.
. But For the first inherited method , I didn't have to annotate arguments with #RequestParam and it worked just fine.
As for the second method I tried at first :
#Override
public ExtContrat getContratById(Long idContrat){
Preconditions.checkArgument(idContrat !=null);
return mapper.map(contratService.get(idContrat),ExtContrat.class);
}
. I expected the same behaviour Like the first Method, But i was wrong and the code ended up firing an IllegalArgumentException because of the check in ligne Preconditions.checkArgument(idContrat!=null).
My question is what is so specific about #PathVariable that i've missed ?
Or is it just something is wrong with my approach?
Thanks.
There is difference between Request param and path variable,seee below post that you can confirm with your uri the cause for the exception :
#PathVariable is to obtain some placeholder from the uri (Spring call it an URI Template) — see Spring Reference Chapter 16.3.2.2 URI Template Patterns
#RequestParam is to obtain an parameter — see Spring Reference Chapter 16.3.3.3 Binding request parameters to method parameters with #RequestParam
Assume this Url http://localhost:8080/SomeApp/user/1234/invoices?date=12-05-2013 (to get the invoices for user 1234 for today)
#RequestMapping(value="/user/{userId}/invoices", method = RequestMethod.GET)
public List<Invoice> listUsersInvoices(
#PathVariable("userId") int user,
#RequestParam(value = "date", required = false) Date dateOrNull) {
...
}

Categories

Resources