8

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

  1. How to create different types of http calls using @ResourceMapping
  2. Can we use @RequestMapping instead of @ResourceMapping in Liferay Spring portlet for REST calls
  3. How can we create resource based REST urls like getUser/12/mumbai
  4. How can we send REST json as body instead of Request Param
Mahozad
  • 18,032
  • 13
  • 118
  • 133
Alex Man
  • 4,746
  • 17
  • 93
  • 178
  • Well did you solve your problem? And if yes what did you do? – Christos Baziotis Oct 25 '15 at 11:29
  • @ChristosBaziotis When I tried with@RequestMapping I 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]` – Alex Man Oct 26 '15 at 07:10
  • @ChristosBaziotis do I need to use any extra configuration for using `@RequestMapping` – Alex Man Oct 26 '15 at 07:11
  • @ChristosBaziotis As Tomáš Piňos said I have created a separate controller class and annotataed as `@Controller` and have created a `@RequestMapping` function like as above, now I am getting 404 – Alex Man Oct 26 '15 at 07:30
  • Please tell me the 1) whole URL you used before and 2) the `@RequestMapping` value that you use now. The `@RequestMapping` must have placed the endpoint in a different URL. For example if before you used `localhost:12345/api/getSomething`, maybe now the endpoint is under `localhost:12345/getSomething` or the opposite. – Christos Baziotis Oct 26 '15 at 11:03
  • @ChristosBaziotis The URL i have used to access before when I used @ResourceMapping is `http://localhost:8082/web/guest/welcome?p_p_id=fileprocessorportlet_WAR_fil%E2%80%A6e=view&p_p_cacheability=cacheLevelPage&p_p_col_id=column-1&p_p_col_count=3&p_p_resource_id=userDetail&userId=1`. Now I have My controller class as [SampleRESTFullController.java](https://gist.github.com/nidhishkrishnan/cc71de6b7dc7c0cebc46) and I have tries to call it as `http://localhost:8082/file-processor-portlet/services/auth/user`but got 404 – Alex Man Oct 26 '15 at 11:28
  • Check under `http://localhost:8082/auth/user` or `http://localhost:8082/file-processor-portlet/auth/user` or `http://localhost:8082/services/auth/user`. I don't know how you have configured your app. – Christos Baziotis Oct 26 '15 at 11:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/93358/discussion-between-alex-man-and-christos-baziotis). – Alex Man Oct 26 '15 at 12:22

4 Answers4

3

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).

Tomas Pinos
  • 2,812
  • 14
  • 22
  • @TomasPinos I have created a separate controller class and annotataed as `@Controller` and have created a `@RequestMapping` function like as above, now I am getting 404 – Alex Man Oct 26 '15 at 07:31
  • Can you come for a chat at http://chat.stackoverflow.com/rooms/93360/discussion-on-liferay-spring-portal-resourcemapping-cant-trigger-different-type – Alex Man Oct 26 '15 at 12:47
2
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
    }
shazin
  • 21,379
  • 3
  • 54
  • 71
  • 1
    When I tried with@RequestMapping I 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]` – Alex Man Oct 19 '15 at 09:50
  • Are you having any duplicate mappings? Please show your full code. – shazin Oct 19 '15 at 09:58
  • @shakin can you tell me your email address – Alex Man Oct 19 '15 at 10:10
  • @shakin any idea on this, hope you have seen my code – Alex Man Oct 20 '15 at 04:12
2

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 > (...
Christos Baziotis
  • 5,845
  • 16
  • 59
  • 80
2

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;
Vasim Akram
  • 80
  • 1
  • 9