I need documentation about #PostAuthorize method in springboot - java

#PostAuthorize("returnObject.idea == authentication.idea.ideatorId")
#RequestMapping(value = "/edit/ideaDetails", method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> editIdeaByIdeaId(#RequestBody final IdeaDTO idea) throws IOException {
return new ResponseEntity<>(this.facade.editIdeaByIdeaId(idea), HttpStatus.OK);
}
I write only the above code in my controller . The idea is the object of my Idea class and ideatorId is the id for one who creates the idea. My intention is to display only the idea details owned by a particular ideatorId. Is there any other configuration required to use #PostAuthorize or by simply write the above code.

Related

MongoDB and Spring MVC: PATCH operation?

I have the following simple Java Controller with the Spring Web framework:
#RestController
#RequestMapping("/rounds")
#Slf4j
public class RoundController {
private RoundService roundService;
#Autowired
public RoundController(RoundService roundService) {
this.roundService = roundService;
}
#GetMapping(
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public List<Round> find() {
return roundService.find();
}
#GetMapping(
path = "/{userId}",
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
public List<Round> get(#PathVariable String userId) {
return roundService.getRoundsByUserId(userId);
}
#PostMapping(
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.CREATED)
public Round create(#Valid #NotNull #RequestBody Round round) {
roundService.create(round);
return round;
}
#DeleteMapping(
path = "/{id}",
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.OK)
public void delete(#PathVariable String id) {
ObjectId objectId = new ObjectId(id);
roundService.delete(objectId);
}
}
When using Mongo is there a best practice for doing an update / patch for an object?
Is it best to just use the POST method, and re-save the Round object in the DB with the changes the user has made?
According to me, the best practice should not be the use of the POST for doing an update/patch.
Keep you POST to do only Round creation.
If you use spring data mongodb just call the save method of your repository with your entity
see https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/repository/MongoRepository.html
For an Update, better to add PUT /{roundId} in your controller and either :
call your save method if you have all the Round data
call a findById to have the full data and set the data you want to change, then save (but this is more like a PATCH)
Or you can also add a PATCH /{roundId} and update just the field you want in your document
see https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/MongoTemplate.html

how to achieve Dynamic URL in REST WebService

I am trying to develop new web service for my application.
For that I am using Spring REST-webservice.
In the controller end, I am trying to fetch the list of records based on the agent passed.Now the requirement , the agent can be passed or it can be null.In case of null agent all records should be selected.else only those records to be fetched.
Tried using below code for achieving dynamism., as per one of the search result however it is not working.
#RequestMapping(value = "/staging/{agentCode: [^/]*?}" , method =
RequestMethod.GET)
Here is my existing code:
#Controller
#RequestMapping(value="/batches")
public class BatchController {
#SuppressWarnings({ "rawtypes" })
#RequestMapping(value="/staging/{agentCode}", method =
RequestMethod.GET)
#ResponseBody
public ResponseEntity IntmBatch(#PathVariable("agentCode") String
agentCode)
{
//code here
}
CASE 1:when I use URL like .,
www.example.com/myapplication/batches/staging/1234
it works fine and desired result is fetched.
CASE 2:However in case I am not passing any parameter say.,
www.example.com/myapplication/batches/staging/
where in , I am not passing any parameter., it says me mapping not found.
Can you please let me know how to achieve this dynamic URL in REST GET Request Method Call.
Thanks in advance!!
Instead of using #Pathvariable you can use #RequestParam for optional values in URL.
So your URL will be like.
CASE 1 : www.example.com/myapplication/batches/staging?agentCode=1234 &
CASE 2 : www.example.com/myapplication/batches/staging
Hope it will work solve your issue.
#SuppressWarnings({ "rawtypes" })
#RequestMapping(value="/staging", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity IntmBatch(#RequestParam(name="agentCode",required=false) String agentCode)
{
//code here
}
create one more method in controller with #RequestMapping(value = "/staging", method = RequestMethod.GET) as follows.
#RequestMapping(value = "/staging", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity returnAll() {
System.out.println("returning all ");
// code here
return null;
}

Java/Spring > Handle Bad Request response for controller method with #RequestBody when no body is sent in request

long story short: I'm creating API that is supposed to be 100% REST.
I'm trying to overwrite default response for the following case:
I've got a method in my #RestController that has #RequestBody as an attribute
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public Resource<User> registerClient(#RequestBody User user, HttpServletRequest request)
and the method is working just fine if I send a proper request. But there is a problem when I don't. When a request has empty body, I get a generic Tomcat error page for status 400 and I need it to send just a string or a JSON object instead.
So far I tried to add Exception Handlers in my RestControllerAdvice for all Spring exceptions from package org.springframework.web.binding, but it didn't work either.
I'm already aware that for some security-related errors one have to create handlers in configuration, but I don't know if this is the case.
Did anyone face similar issues? Is there something I'm missing?
The solution was to simply put required = false in RequestBody annotation. After that, I could easily add some logic to throw custom exception and handle it in ControllerAdvice.
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public Resource<User> registerClient(#RequestBody(required = false) User user, HttpServletRequest request){
logger.debug("addClient() requested from {}; registration of user ({})", getClientIp(request), user);
if(user == null){
throw new BadRequestException()
.setErrorCode(ErrorCode.USER_IS_NULL.toString())
.setErrorMessage("Wrong body or no body in reqest");
} (...)
Firstly I suggest you to use BindingResult as a parameter of the POST call and check if it returns an error or not.
#RequestMapping(value = {"register"}, method = RequestMethod.POST, produces = "application/hal+json")
public ResponseEntity<?> registerClient(#RequestBody User user, HttpServletRequest request, BindingResult brs)
if (!brs.hasErrors()) {
// add the new one
return new ResponseEntity<User>(user, HttpStatus.CREATED);
}
return new ResponseEntity<String>(brs.toString(), HttpStatus.BAD_REQUEST);
}
Secondly, the call can throw some of errors, a good practice is to carch them and return them itself or transform them to your own exception object. The advantage is it secures a call of all the update/modify methods (POST, PUT, PATCH)
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return new ResponseEntity<List<MethodArgumentNotValidException>>(e, HttpStatus.BAD_REQUEST);
}
#ExceptionHandler({HttpMessageNotReadableException.class})
#ResponseBody
public ResponseEntity<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
return new ResponseEntity<List<HttpMessageNotReadableException>>(e, HttpStatus.BAD_REQUEST);
}
Your control will never reach to your request method under normal circumstances.
If you want a looking good page you can make use of web.xml and configure it to produce your answer.
<error-page>
<error-code>404</error-code>
<location>/pages/resource-not-found.html</location>
</error-page>
Generally, if you want to go past this 400 problem, you will have to add a few annotiations to your User.java to avoid any unknown fields while de-serializing.

Unit test for a header created inside controller

In my Spring Boot application, I'm adding a header inside my controller:
#RequestMapping(value = "/people", method = RequestMethod.GET)
public ResponseEntity<List<Person>> systems(Pageable pageable) {
Page people = peopleService.getPeopleSystems(pageable);
HttpHeaders headers = new HttpHeaders();
headers.add("CustomHeader1", String.valueOf(people.getTotalElements()));
headers.add("CustomHeader2", String.valueOf(people.getSize()));
return new ResponseEntity<List<People>>(people.getContent(), headers, HttpStatus.OK);
}
The header contains the quantity of people which is obtained inside controller.
The code works as expected, but isn't testable.
The test class roughly could be like this:
#Test
public void test_header() throws Exception {
PeopleService pepleService = mock(PeopleService.class);
Pageable pageable = mock(Pageable.class);
Page<Person> page = mock(Page.class);
when(pepleService.getPeopleSystems(pageable)).thenReturn(page);
when(page.getTotalElements()).thenReturn((long) 2000);
when(page.getSize()).thenReturn(10);
mockMvc.perform(get("/people")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(header().stringValues("CustomHeader1"));
}
The test fails at line
headers.add("CustomHeader1", String.valueOf(people.getTotalElements()));
with NPE exception.
So my question is how to make my controller testable, or how to refactor the code to get rid of creating the new HttpHeaders() inside my method.
Any help would be apreciated.
Turned out that all what I needed was to indicate that the class was instance of Pagable.class. Here is the working test:
Page<Person> page = new PageImpl<>(new ArrayList<>());
when(pepleService.getPeopleSystems(any(Pageable.class))).thenReturn(page);
mockMvc.perform(get("/people")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(header().string("CustomHeader1", "2000"));
You need to mock peopleService and then mock method peopleService.getPeopleSystems(pageable) to return mock of Page.
Then you need to mock methods people.getTotalElements() and people.getSize() to return proper values.
HttpHeaders headers = mock(HttpHeaders.class) this should not work because new object is created in method.

Can #PathVariable return null if it's not found?

Is it possible to make the #PathVariable to return null if the path variable is not in the url? Otherwise I need to make two handlers. One for /simple and another for /simple/{game}, but both do the same just if there is no game defined i pick first one from a list however if there is a game param defined then i use it.
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable("example") String example,
HttpServletRequest request) {
And this is what I get when trying to open page /simple:
Caused by: java.lang.IllegalStateException: Could not find #PathVariable [example] in #RequestMapping
They cannot be optional, no. If you need that, you need two methods to handle them.
This reflects the nature of path variables - it doesn't really make sense for them to be null. REST-style URLs always need the full URL path. If you have an optional component, consider making it a request parameter instead (i.e. using #RequestParam). This is much better suited to optional arguments.
As others have already mentioned No you cannot expect them to be null when you have explicitly mentioned the path parameters. However you can do something like below as a workaround -
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable Map<String, String> pathVariablesMap,
HttpServletRequest request) {
if (pathVariablesMap.containsKey("game")) {
//corresponds to path "/simple/{game}"
} else {
//corresponds to path "/simple"
}
}
If you are using Spring 4.1 and Java 8 you can use java.util.Optional which is supported in #RequestParam, #PathVariable, #RequestHeader and #MatrixVariable in Spring MVC
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable Optional<String> game,
HttpServletRequest request) {
if (game.isPresent()) {
//game.get()
//corresponds to path "/simple/{game}"
} else {
//corresponds to path "/simple"
}
}
You could always just do this:
#RequestMapping(value = "/simple", method = RequestMethod.GET)
public ModelAndView gameHandler(HttpServletRequest request) {
gameHandler2(null, request)
}
#RequestMapping(value = "/simple/{game}", method = RequestMethod.GET)
public ModelAndView gameHandler2(#PathVariable("game") String game,
HttpServletRequest request) {
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable(value="example",required = false) final String example)
Try this approach, it worked for me.
I just tested this just now, but by combining the above solution i got this:
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable(value = "game", required = false) String example,
HttpServletRequest request) {
if (example != null) {
//...
} else {
//pick first, ...
}
}
Now when you use "/simple", String example will be null instead of throwing Exception.
Short solution, no fancy Optional<> or Map<>
We can write multiple methods in controllers with explicit mapping with the path variable combination to exclude the optional variables (if using old version of Spring)
In my scenario wanted to develop an API to get recycle value for old device where parameters could be brand, model and network however network is an option one.
One option to handle this was use network as a request parameter instead of pathVariable.
for e.g. /value/LG/g3?network=vodafone however I didn't like this approach.
for me the more cleaner one was to use below
/refurbValue/LG/g3
/refurbValue/LG/g3/vodafone
#RequestMapping(value = "/refurbValue/{make}/{model}/{network}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
def getRefurbValueByMakeAndModelAndNetwork(#PathVariable String make, #PathVariable String model, #PathVariable String network ) throws Exception {
//logic here
}
#RequestMapping(value = "/refurbValue/{make}/{model}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
def getRefurbValueByMakeAndModel(#PathVariable String make, #PathVariable String model) throws Exception {
//logic here
}
In the above example, both controller can use the same service method and handling of the parameter can be done. In my case I was using Groovy so it was easy to use with optional parameter like
Map getRefurbValue(String brand, String model, String network="")

Categories