Khai No Khai No - 6 months ago 161
Java Question

Pathvariable on Controller class level does not work Spring Restful

I have a controller where I have controller level mapping as "/subjects/{subjectId}/lessons" which is required due to some reason

@RestController
@RequestMapping( value = "/subjects/{subjectId}/lessons",produces = { MediaType.APPLICATION_JSON_VALUE } )
public class LessonController {
.......
@RequestMapping( method = RequestMethod.GET )
public ResponseEntity<Resources<Resource<Lesson>>> getAllSjubject(@PathVariable(value = "subjectId") int subjectId){

List<Lesson> lessonList = lessonService.getAllLessons(subjectId);
Resources<Resource<Lesson>> resource = this.lessonResourceAssembler.toLessonResourceList(lessonList);
return new ResponseEntity<Resources<Resource<Lesson>>>(resource, HttpStatus.OK);
}
}


When made request it throws the exception

java.lang.IllegalArgumentException: Not enough variable values available to expand 'subjectId'
at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:327)
at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:230)
at org.springframework.web.util.HierarchicalUriComponents$FullPathComponent.expand(HierarchicalUriComponents.java:685)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:328)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:47)
at org.springframework.web.util.UriComponents.expand(UriComponents.java:163)
at org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo(ControllerLinkBuilder.java:89)
at org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo(ControllerLinkBuilder.java:69)
at com.khaino.springrest.assembler.LessonResourceAssembler.toLessonResource(LessonResourceAssembler.java:31)
at com.khaino.springrest.assembler.LessonResourceAssembler.toLessonResourceList(LessonResourceAssembler.java:44)
at com.khaino.springrest.controller.LessonController.getAllSjubject(LessonController.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:111)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:806)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:729)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1526)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1482)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)


But when I move the pathvariable to method level as below it is working correctly.

@RequestMapping( value = "/subjects",produces = { MediaType.APPLICATION_JSON_VALUE } )
public class LessonController {

......

@RequestMapping( value = "/{subjectId}/lessons", method = RequestMethod.GET )
public ResponseEntity<Resources<Resource<Lesson>>> getAllSjubject(@PathVariable("subjectId") int subjectId){

List<Lesson> lessonList = lessonService.getAllLessons(subjectId);
Resources<Resource<Lesson>> resource = this.lessonResourceAssembler.toLessonResourceList(lessonList);
return new ResponseEntity<Resources<Resource<Lesson>>>(resource, HttpStatus.OK);
}

....
}


Something wrong with my code? What could be the reason and solution?

Answer

Your mappings works in both scenarios but based on your stack trace, the following part is the source of problem:

this.lessonResourceAssembler.toLessonResourceList(lessonList)

You're creating some Hypermedia Links that contains some Template Variables which are NOT expanded.

Updated: Use this approach to create link:

linkTo(methodOn(LessonController.class).getAllSjubject(42))
                .withRel(REL_SELF)

mehtodOn is in org.springframework.hateoas.mvc.ControllerLinkBuilder package, make sure you have correct static imports.

Comments