Is it possible to return value from Spring AspectJ? - spring

I'm returning value from my controller. Let it be ResponseEntity<String> type.
Controller returns:
new ResponseEntity<String>("{\"msg\":\"success\"}",HttpStatus.OK);
Following value goes to my spring aspect. I am receiving this object in following code:
#AfterReturning(pointcut = "somePointcut()",returning = "retVal")
public ResponseEntity<String> adviceTest3(Object retVal) {
//here i have access to controller's object
return new ResponseEntity<String>("{\"msg\":\"changed value within aspect\"}",HttpStatus.OK);
}
I'm aware that there is #AfterReturning. Is it any way manipulate data and achieve that?

With #AfterReturning, no. Quote from the documentation
An after returning advice has access to the return value (which it cannot modify), invoked method, methods arguments and target.
You could use #Around instead.

Related

How to just get the data using CRUD POST method?

I have developed Small Spring boot Rest api app. I can able to get the data or create new record and search with paging and sorting.
Now i'm looking for provide input data in body to get the data instead of providing in URL with GET method. Is this method also default function ? Please advise.
public interface CodeTextRepository extends PagingAndSortingRepository<CodeText, Long> {
}
How to write POST method to just get the data ?
http://localhost:8080/api/code
method : POST
{
"code":1
}
If I understand you correctly, you want to create a controller that will get the a model as body parameter ({ "code": 1 }) in a POST method and then do something with it.
To do that, you can create a controller that looks like the following (I inserted pseudo-code as an example):
#RestController
#RequestMapping(value = "/api/code")
public class CodeTextController {
private CodeTextRepository codeTextRepository;
// constructor injection
public CodeTextController(CodeTextRepository codeTextRepository) {
this.codeTextRepository = codeTextRepository;
}
#PostMapping
public CodeText postCodeText(#RequestBody CodeTextRequest codeTextRequest) {
// some code to get from the DB
return codeText;
}
}
public class CodeTextRequest {
private int code;
// getters and setters
}
Simply add Accept header to the request, like
accept: application/json
Spring Data-Rest will return the body after a POST request if either the returnBodyOnCreate flag was explicitly set to true in the RepositoryRestConfiguration OR if the flag was NOT set AND the request has an Accept header.
You can set the flag directly during configuration, or you can set it via the application.properties:
spring.data.rest.returnBodyOnCreate = true
you can also set it separately for update:
spring.data.rest.returnBodyOnUpdate = true
---- edit
Maybe I misunderstood your question. If you simply want to GET an existing data using POST method, then DO NOT DO IT AT ALL! That's not a REST API any more. There must be some reason you want to do it, but you should try do resolve that original problem instead in another way!

How to get Swagger UI to display similar Spring Boot REST endpoints?

I have a controller class with two endpoints
#RestController
#RequestMapping
public class TestController {
#RequestMapping(
value= "/test",
method = RequestMethod.GET)
#ResponseBody
public String getTest() {
return "test without params";
}
#RequestMapping(
value= "/test",
params = {"param"},
method = RequestMethod.GET)
#ResponseBody
public String getTest(#PathParam("param") int param) {
return "test with param";
}
}
One has a parameter, one doesn't, and the both work.
If I use curl or a web browser to hit the endpoints
http://localhost:8081/test
returns
test without params
and
http://localhost:8081/test?param=1
returns
test with param
but the swagger ui only shows the one without a parameter.
If I change the value in the request mapping for the request with a parameter to
#RequestMapping(
value= "/testbyparam",
params = {"param"},
method = RequestMethod.GET)
Swagger UI displays both endpoints correctly, but I'd rather not define my endpoints based on what swagger will or won't display.
Is there any way for me to get swagger ui to properly display endpoints with matching values, but different parameters?
Edit for Clarification:
The endpoints work perfectly fine; /test and /test?param=1 both work perfectly, the issue is that swagger-ui won't display them.
I would like for swagger ui to display the endpoints I have defined, but if it can't, then I'll just have to live with swagger-ui missing some of my endpoints.
Edit with reference:
The people answering here: Proper REST formatted URL with date ranges
explicitly say not to seperate the query string with a slash
They also said "There shouldn't be a slash before the query string."
The issue is in your Request Mapping, The second method declaration is overriding the first method. As Resource Mapping value is same.
Try changing the second method to below. As you want to give input in QueryParam rather than path variable, you should use #RequestParam not #PathParam.
Note that you have to give /test/, In order to tell Spring that your mapping is not ambiguous. Hope it helps.
#RequestMapping(
value= "/test/",
method = RequestMethod.GET)
#ResponseBody
public String getTest (#RequestParam("param") int param) {
return "test with param"+param;
}
Upon reading clarifications, the issue here is that swagger-ui is doing the correct thing.
You have two controller endpoints, but they are for the same RESOURCE /test that takes a set of optional query parameters.
Effectively, all mapped controller endpoints that have the same method (GET) and request mapping (/test) represent a single logical resource. GET operation on the test resource, and a set of optional parameters which may affect the results of invoking that operation.
The fact that you've implemented this as two separate controller endpoints is an implementation detail and does not change the fact that there is a single /test resource that can be operated upon.
What would be the benefit to consumers of your API by listing this as two separate endpoints in swagger UI vs a single endpoint with optional parameters? Perhaps it could constrain the set of allowed valid query parameters (if you set ?foo you MUST set &bar) but this can also be done in descriptive text, and is a much more standard approach. Personally, I am unfamiliar with any publicly documented api that distinguishes multiple operations for the same resource differentiated by query params.
As per Open API Specification 3
OpenAPI defines a unique operation as a combination of a path and an
HTTP method. This means that two GET or two POST methods for the same
path are not allowed – even if they have different parameters
(parameters have no effect on uniqueness).
Reference - https://swagger.io/docs/specification/paths-and-operations/
This was also raised as an issue but it was closed because OAS3 doesn't allow that -
https://github.com/springdoc/springdoc-openapi/issues/859
Try including the param in the path as below.
#GetMapping("/test/{param}")
public String getTest(#PathVariable final int param) {
return "test with param";
}
I'm unclear exactly what you're attempting to do, but I'll give two solutions:
If you want to have PATH parameters e.g. GET /test & GET /test/123 you can do:
#GetMapping("/test")
public String getTest() {
return "test without params";
}
#GetMapping("test/{param}")
public String getTest(#PathVariable("param") int param) {
return "test with param";
}
If you want query parameters (GET /test and GET /test?param=123) then you need a single endpoint that takes an optional parameter:
#GetMapping("test")
public String getTest(#RequestParam("param") Integer param) {
if(param == null) {
return "test without params";
} else {
return "test with param";
}
}

Using postman raw testing spring-boot controller failed

Here is the controller:
Here is the postman:
Via form-data, I can get caseId in my controller.
But raw with header, I can't.
I don't know why... Is there anything wrong with my controller ?
Please help, thanks.
edit 1:
Yeah. Add something more
We know, springMVC will bind data for us, but when we use POST request and put data in body via raw and Content-Type:application/json, spring will still bind data? request.getInputStream() will only call once.
edit 2:
I found a way to get the raw.
get the json string.
edit in 11/29/2017
I found that:
Post with raw, I need to use #RequestBody to recive the value.
Here are the example of how to retrieve data using POSTMAN and bind with SpringMVC
#RequestMapping(value = "/user/", method = RequestMethod.GET)
public ResponseEntity<List<User>> listAllUsers() {
List<User> users = userService.findAllUsers();
if (users.isEmpty()) {
return new ResponseEntity(HttpStatus.NO_CONTENT);
// You many decide to return HttpStatus.NOT_FOUND
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
You may refer to this article : Spring Boot Rest API Example
Can u bind request param as below and check :
public Object getTcaseByCaseId(#RequestParam("caseId") String caseId) {

Command object automatically added to model?

I have a controller method like this:
#RequestMapping("/hello")
public String hello(UserForm user) {
return "hello";
}
It receives some request parameters in the UserForm command object. But I have not written any code to add the object to the Model. Still, in the view hello.jsp, I'm able to access the data, like this:
Hello, ${userForm.name}!
Does it mean that Spring MVC adds command objects to the Model automatically?
You don't need #ModelAttribute just to use a Bean as a parameter.
You'll need to use #ModelAttribute or model.addAttribute() to load default data into your model - for example from a database.
Most of the Spring controllers in the real world accept a lot of different types of parameters - Path variables, URL parameters, request headers, request body and sometimes even the entire HTTP Request object. This provides a flexible mechanism to create APIs. Spring is really good at parsing these parameters in to Java types as long as there is an ObjectMapper (like Jackson) configured to take care of the de-serialization.
The RequestMappingHandlerAdapter makes sure the arguments of the method are resolved from the HttpServletRequest.
Spring model data created prior to (or during) the handler method
execution gets copied to the HttpServletRequest before the next view
is rendered.
By now, Spring has processed the HTTP request and it creates the ModelAndView object from the method’s return value. Also, note that you are not required to return a ModelAndView instance from a controller method. You may return a view name, or a ResponseEntity or a POJO that will be converted to a JSON response etc.
ServletInvocableHandlerMethod invocableMethod
= createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(
this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(
this.returnValueHandlers);
}
The returnValueHandlers object is a composite of HandlerMethodReturnValueHandler objects. There are also a lot of different value handlers that can process the result of your method to create ModelAndViewobject expected by the adapter.
Then, it has to render the HTML page that the user will see in the browser. It does that based on the model and the selected view encapsulated in the ModelAndView object.
Now, at this stage, the view gets access to the userForm (as in your example above) from the request scope.

Spring mvc controller null return handler

#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public List<Country> getListOfCountries() {
return countryService.listAll();
}
It displays a json view of the object but if the service return null, then I want to display an error message, Any suggestions pls?
First of all, even if this does not directly answer the question, your objects should never ever return null instead of empty collections - you can find the reasoning in Effective Java 2nd Edition, Item 43 / p.201
So, if the situation when no countries were found is normal it must be processed by the client JS code that will check the count and display the respective message.
If something has gone wrong you can throw an exception(as Biju has pointed out +1) - I believe that it's the service who should throw the exception because it knows the reason why it happened, and not to return null anyway.
I'd like to add that in Spring 3.2(in pre Spring 3.2 returning response body is complicated) you can set an #ExceptionHandler that will both return JSON and set the HTTP status code which can be later processed by the client. I think that returning a custom JSON response with some error code is most optimal here.
#RequestMapping("/test")
#ResponseBody
public List<Country> getListOfCountries() {
//assuming that your service throws new NoCountriesFoundException();
//when something goes wrong
return countryService.listAll();
}
#ExceptionHandler(NoCountriesFoundException.class)
ResponseEntity<String> test() {
return new ResponseEntity<String>(
"We are sorry, our server does not know any countries yet.",
HttpStatus.I_AM_A_TEAPOT );
}
Then in the JS code, you can do specific processing depending on the returned status code.
Also, to avoid declaration of the same #ExceptionHandler in different controllers, in Spring 3.2 you can put #ExceptionHandler inside a #ControllerAdvice annotated class.
For details, see http://static.springsource.org/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers and http://www.springsource.org/node/3738 for 3.2 specific things
You have a couple of options I think:
If you return a null back, it will be returned as an empty string "", you can probably look for that and handle it.
Return a wrapper type on top of your list, this way if the wrapped list is null something like this will be returned back to the client {"countries":null} which can be more easily handled at the javascript end.
Throw an exception, which will propagate as a 500 status code back to the client, you can then have an error handler on the javascript side to handle this scenario.

Resources