Pass attribute in spring from controller to external url and back - java

I have some external url for payment. After payment is done it redirects to me back.
#RequestMapping(value = "/first", method = RequestMethod.GET)
public String first(#RequestParam("id") String id) {
doSomething(id);
return "redirect:/externalurl"
}
// This method is called from external url
#RequestMapping(value = "/second", method = RequestMethod.GET)
public String second() {
// I want to get here id from first call
}
Is it possible somehow to do it with RedirectAttributes or other similar approach like to add it in first() method and to get it in second() method ?

Related

Spring MVC Accessing Session Attribute in Function in Controller Class

I was having some problem when trying to access session attribute in Controller. I declared my session attributes as such:
#Controller
#SessionAttributes({ WebKeys.OBJECT_SIX, WebKeys.DSP_LOGIC, WebKeys.NEW_CARD_FORM })
In each of my API, I am calling the function:
#RequestMapping(value = "/apiA.do", method = RequestMethod.POST)
public String doAPIa(Model model) {
setInfo(model);
}
#RequestMapping(value = "/apiB.do", method = RequestMethod.POST)
public String doAPIb(Model model) {
setInfo(model);
}
In my setInfo(), I am trying to access the session attribute and add value back to the model:
private void setInfo(Model model) throws Exception{
String populationId = // need to get from WebKeys.OBJECT_SIX session attribute
if(populationId!=null && (populationId.equals(Constants.POP_TYPE_ID))){
DisplayHelperTO helper = (DisplayHelperTO) // need to get from WebKeys.DSP_LOGIC;
NewCardNewBasicForm newCardForm = (NewCardNewBasicForm ) // need to get from WebKeys.NEW_CARD_FORM);
model.addAttribute("newCardForm", newCardForm);
model.addAttribute("dspLogic", helper);
}
}
I tried to declare in such way:
private void setInfo(Model model,
#SessionAttribute(WebKeys.OBJECT_SIX) String populationId) throws Exception{
}
However, if I am declaring the function in this way, the part to call the setInfo() in both doAPIa and doAPIb will be highlighted in syntax error. Any ideas on how to access the session attributes in function? Thanks!

Redirect to an API that has #RequestBody arguments

The below API is called from the UI, as well as it wants to be redirected to, by other APIs.
#RequestMapping(value = "/showAbc", method = RequestMethod.POST)
public ModelAndView showAbc(#RequestBody Abc abcInstance) {
//doSomething
}
The below API is also called from the UI end, but it fetches an instance of Abc class using a repository call and now wants to call the above API using redirect, and also wants to pass the instance as an argument.
#RequestMapping(value = "/showBcd", method = RequestMethod.POST)
public ModelAndView showBcd(#RequestParam String bcdId){
Abc abc = abcRepository.findByBcdId(bcdId);
/* How to pass the instance of Abc, when redirecting to /showAbc */
return new ModelAndView("redirect:/showAbc");
}
Now, in the above redirect I also want to pass the instance of Abc, when redirecting to /showAbc from /showBcd.
I wouldn't recommend calling one API to another if it's in the same application. This way you are tightly coupling API to API. Either leave the API contract to be called to UI or handle the delegation via plain java service calls.
#RequestMapping(value = "/showAbc", method = RequestMethod.POST)
public ModelAndView showAbc(#RequestBody Abc abcInstance) {
return new ModelAndView("abc",abcService.get());
}
#RequestMapping(value = "/showBcd", method = RequestMethod.POST)
public ModelAndView showBcd(#RequestParam String bcdId){
// You should move this repo call to bcdService.java
Abc abc = abcRepository.findByBcdId(bcdId);
//Use a normal service call to get the instance instead of a API call
return new ModelAndView("abc",abcService.getViaInstance(abc));
}
However, to have a model passed to the view(Forward the request), you need to add to the model as :
ModeAndView m = new ModelAndView("forward:/showAbc");
Abc abc = getAbcInstance();
m.addObject("abc",abc);
return m;
Please try this
ModeAndView m = new ModelAndView("redirect:/showAbc");
Abc abc = abcRepository.findByBcdId(bcdId);
m.addObject("abc",abc);
return m;

liferay spring portal #ResourceMapping can't trigger different types of http methods

I have created a liferay portlet application using Spring, thymeleaf and AngularJS. For communication between AngularJS and spring I need to create some rest calls which I have created using #ResourceMapping like as shown below. The application is working fine but the problem is that I don't know how to make GET, DELETE, PUT http REST calls since #ResourceMapping is not allowing to specify any methods.
#ResourceMapping(value="getUserDetail")
public void userDetail(#RequestParam long userId, ResourceResponse response) throws Exception {
Users users = new Users(userId);
// some logic
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
JSON_MAPPER.writeValue(response.getPortletOutputStream(), users);
}
When I used #RequestMapping instead of #ResourceMapping like as shown below
#RequestMapping(value="getUserDetail", method=RequestMethod.GET)
#ResponseBody
public void userDetail(#RequestParam long userId, ResourceResponse response) throws Exception {
System.out.println("Got detail request for user with id {} "+ userId);
// UserDetail userDetail = this.userService.getPortalUserDetail(userId);
List<String> users = new ArrayList<String>();
users.add("Manu");
users.add("Lissie");
users.add("John");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
JSON_MAPPER.writeValue(response.getPortletOutputStream(), users);
}
I have got
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping': Initialization of bean failed; nested exception is java.lang.IllegalStateException: Mode mappings conflict between method and type level: [getUserDetail] versus [view]
Can anyone please tell me some solution for this
How to create different types of http calls using #ResourceMapping
Can we use #RequestMapping instead of #ResourceMapping in Liferay Spring portlet for REST calls
How can we create resource based REST urls like getUser/12/mumbai
How can we send REST json as body instead of Request Param
Mode mappings conflict exception
The question doesn't show it, but your controller probably has #RequestMapping("view") annotation. This type level mapping is in conflict with the method level mappings. You should remove #RequestMapping annotation on the controller class.
Request mapping examples
#Controller
public class SampleRESTFullController {
// Simple GET
#RequestMapping(value = "/helloSample", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public #ResponseBody List<HelloSample> helloSample() { ... }
// GET with path variable
#RequestMapping(value = "/helloSample/sampleId/{sampleId}", method = RequestMethod.GET)
public #ResponseBody HelloSample helloSample(#PathVariable("sampleId") Long sampleId) { ... }
// POST with #RequestBody
#RequestMapping(value = "/helloSample", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public #ResponseBody HelloSample createSample(#RequestBody HelloSample helloSample) { ... }
// PUT with path variable and #RequestBody
#RequestMapping(value = "/helloSample/sampleId/{sampleId}", method = RequestMethod.PUT)
#ResponseStatus(HttpStatus.NO_CONTENT)
void update(#PathVariable("sampleId") long sampleId, #RequestBody HelloSample helloSample) { ... }
// DELETE
#RequestMapping(value = "/helloSample/sampleId/{sampleId}", method = RequestMethod.DELETE)
#ResponseStatus(HttpStatus.NO_CONTENT)
void delete(#PathVariable("sampleId") long sampleId) { ... }
}
I took the examples from Using RESTFul services with Liferay blog post. It answers all your questions and presents tons of examples. Pay attention to Spring configuration, which makes the RESTful services possible (especially the view resolver and message converter).
1. How to create different types of http calls using #ResourceMapping
If you want to a REST Api with Complete Actions (GET, POST, PUT, DELETE) you need to use #RequestMapping.
2. Can we use #RequestMapping instead of #ResourceMapping in Liferay Spring portlet for REST calls
You should be able to use.
3. How can we create resource based REST urls like getUser/12/mumbai
#RequestMapping(value="getUser/{userId}/mumbai", method=RequestMethod.GET)
#ResponseBody
public List<String> userDetail(#RequestParam("userId") long userId) throws Exception {
System.out.println("Got detail request for user with id {} "+ userId);
//UserDetail userDetail = this.userService.getPortalUserDetail(userId);
List<String> users = new ArrayList<String>();
users.add("Manu");
users.add("Lissie");
users.add("John");
return users;
}
4. How can we send REST json as body instead of Request Param
You can use #RequestBody
#RequestMapping(value="saveUser/{userId}", method=RequestMethod.GET)
#ResponseStatus(HttpStatus.CREATED)
public void userDetail(#RequestParam("userId") long userId, #RequestBody User user) throws Exception {
// Logic
}
How to create different types of http calls using #ResourceMapping
Here are some examples that may help you, that's how i use #RequestMapping:
// GET
#RequestMapping(value = "/api/something", method = RequestMethod.GET)
#ResponseBody
public boolean getSomething() {
return "something";
}
// GET with param
#RequestMapping(value = "/api/something/{id}", method = RequestMethod.GET)
#ResponseBody
public boolean getSomething(#PathVariable("id") Long id) {
return id;
}
Instead of RequestMethod.GET you can use RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE and so on...
How can we send REST json as body instead of Request Param
Here is a code snippet that i currently use with an AngularJS FrontEnd for user registration. It works just fine and i use #RequestMapping:
#ResponseBody
#RequestMapping(value = "/auth/register", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> register(#RequestBody User user) {
user = userService.initUser(user);
Authentication authentication = securityUserDetailsService.register(user);
if (authentication != null) {
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);
User authUser = securityUserDetailsService.getAuthenticatedUser();
return new ResponseEntity<>(authUser, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
In order to consume JSON you do:
RequestMapping(value = "/whatever", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
In order to produce (return) JSON you do:
RequestMapping(value = "/whatever", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
Also since you use Spring i think you should take a look at Spring Data and Spring Data Rest. This way you can expose your business models as RESTful endpoints.
How can we create resource based REST urls like getUser/12/mumbai
So in order to expose this endpoint getUser/12/mumbai that's what you should do:
// mumbai hardcoded
#RequestMapping(value = "/getUser/{id}/mumbai", method = RequestMethod.GET)
#ResponseBody
public User getUser(#PathVariable("id") Long id) {
// go get the user ...
return user;
}
// mumbai as a param
#RequestMapping(value = "/getUser/{id}/{prop}", method = RequestMethod.GET)
#ResponseBody
public User getUser(#PathVariable("id") Long id, #PathVariable("prop") String prop) {
// go get the user ...
return user;
}
Lastly can you please try to change
public void userDetail (...
to this
public ResponseEntity<userDetail > (...
There are following methods to use rest app with angular js
#RequestMapping(value = "/saveuser", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#RequestMapping(value = "/getemployee", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#RequestMapping(value = "/editCountry", method = RequestMethod.PUT)
#RequestMapping(value = "/deleteCountry", method = RequestMethod.DELETE)
and use following javascript to communicate with spring controller
var formData = {
"userName" : 'Vasim',
"password" : '123456',
"roleName" : 'Admin'
};
var response = $http.post('add', formData);
response.success(function(data, status, headers, config) {
$scope.message = data;
});
var formData = {
"userName" : 'Vasim',
"password" : '123456',
"roleName" : 'Admin'
};
var response = $http.put('edit', formData);
response.success(function(data, status, headers, config) {
$scope.message = data;
});
$scope.delete= function(employeeId) {
$http['delete']('delete', {
params : {
'employeeId' : employeeId
}
}).
success(function(data) {
$scope.msg = data;
});
$http.get('get',{params:{
'id':id
}
}).success(function(data) {
$scope.employees = data;

Spring RequestMapping conflicts

I have a RequestMapping that displays a grid, and another one for loading objects in grid.
#RequestMapping(value = "/grid/{objType}", method = RequestMethod.GET)
public String displayGrid(Model model, #PathVariable("objType") String objType) {
// some code here
}
#RequestMapping(value = "/loadGrid", method = RequestMethod.GET)
public #ResponseBody String loadGrid(Model model) {
// returns a JSON
}
When i display the grid the url is like ../grid/User
The problem is that after the grid is created and a request loadGrid is made, the request is mapped to /grid/loadGrid which is resolved by the first method instead of the second one.
Is there any way to make a request for /grid with nothing after it ?
Or any way to resolve this conflict ?
The collision isn't a problem; spring resolves exact matches first. (see the source code of AbstractHandlerMethodMapping)
Your problem is that you've incorrectly defined your mappings. If you define a #RequestMapping at the class level, all the method #RequestMappings will be prefixed with the defined value.
The following maps three endpoints: /grid, /grid/{objType} and /grid/loadGrid. Note that the #RequestMapping for get() defines no value, only its method because it inherits from the class-level annotation.
#Controller
#RequestMapping(value = "/grid")
public class GridController {
#RequestMapping(method = RequestMethod.GET)
public String get(Model model) {
// ...
}
#RequestMapping(value = "/{objType}", method = RequestMethod.GET)
public String displayGrid(Model model, #PathVariable("objType") String objType) {
// ...
}
#ResponseBody
#RequestMapping(value = "/loadGrid", method = RequestMethod.GET)
public String loadGrid(Model model) {
// ...
}
}

How to read flash attributes after redirection in Spring MVC 3.1?

I would like to know how to read a flash attributes after redirection in Spring MVC 3.1.
I have the following code:
#Controller
#RequestMapping("/foo")
public class FooController {
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(...) {
// I want to see my flash attributes here!
}
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public ModelAndView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new ModelAndView().setViewName("redirect:/foo/bar");
}
}
What I am missing?
Use Model, it should have flash attributes prepopulated:
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(Model model) {
String some = (String) model.asMap().get("some");
// do the job
}
or, alternatively, you can use RequestContextUtils#getInputFlashMap:
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(HttpServletRequest request) {
Map<String, ?> inputFlashMap = RequestContextUtils.getInputFlashMap(request);
if (inputFlashMap != null) {
String some = (String) inputFlashMap.get("some");
// do the job
}
}
P.S. You can do return return new ModelAndView("redirect:/foo/bar"); in handlePost.
EDIT:
JavaDoc says:
A RedirectAttributes model is empty when the method is called and is
never used unless the method returns a redirect view name or a
RedirectView.
It doesn't mention ModelAndView, so maybe change handlePost to return "redirect:/foo/bar" string or RedirectView:
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public RedirectView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new RedirectView("/foo/bar", true);
}
I use RedirectAttributes in my code with RedirectView and model.asMap() method and it works OK.
Try this:
#Controller
public class FooController
{
#RequestMapping(value = "/foo")
public String handleFoo(RedirectAttributes redirectAttrs)
{
redirectAttrs.addFlashAttribute("some", "thing");
return "redirect:/bar";
}
#RequestMapping(value = "/bar")
public void handleBar(#ModelAttribute("some") String some)
{
System.out.println("some=" + some);
}
}
works in Spring MVC 3.2.2
For all those like me who were having problems with seeing the POST url in the browser when a validation would fail.
The POST url is a private url that should not be exposed to users but it was automatically rendered when a validation failed. i.e. if a field was below a minimum length. I was using #Valid. I wanted the original GET url of the form to show at all times even when validation bounced back to the form, so I did the following:
if (validation.hasErrors()) {
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.story", validation);
redirectAttributes.addFlashAttribute("story", story);
return new ModelAndView("redirect:/january/2015");
where story is the form object representation, redirectAttributes are RedirectAttributes you put in the method signature and validation is the BindingResult. /january/2015 is the mapping to the GET controller where the form lives.
After this implementation, in the mapping for /january/2015, story comes in intact as follows:
Story story= (Story) model.asMap().get("story");
//story from the POST method
I had to augment my GET method and check if this was not null. If not null, then send this to the form else I would send a newly initialized Story type to the form as default behaviour before.
In this manner, I am able to return to the form with the bindingresults intact (errors show on form) but have my GET url in place of the post url.

Categories

Resources