문제

I'm using Spring MVC for my REST server. The spring.version in my pom.xml is 3.2.1.RELEASE.

I have created a number of RESTful APIs and used PathVariables extensively. It works fine.

But it seems to break in the following scenario. If I have something like the following, my REST request doesn't find a resource.

@Controller
@RequestMapping(value = { "/resourceA/{resourceAId}/resourceB/{resourceBId}/resourceC/{resourceCId}" })
public class TenderController {

    @RequestMapping(value = "", method = RequestMethod.POST)
    @ResponseBody
    public Tender capture(
        @PathVariable long resourceAId,
        @PathVariable long resourceBId,
        @PathVariable long resourceCId,
        @RequestBody Map<String, Object> requestBody) {

        ...
    }
}

Edited:

Here is my sample failing REST request:

POST /resourceA/1/resourceB/2/resourceC/3 HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache

{ "bodyParam1": 400, "bodyParam2": 0 }

However, if I drop {resourceCId} from the Java code and adjust my REST request accordingly, it successfully finds the resource:

Revised Java code:

@RequestMapping(value = { "/resourceA/{resourceAId}/resourceB/{resourceBId}/resourceC" })

New (successful) REST request:

POST /resourceA/1/resourceB/2/resourceC HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache

{ "bodyParam1": 400, "bodyParam2": 0 }

So essentially as soon as I have 3 path variables, things seem to fall apart. Any ideas on what might be going on here? Have I stumbled into a Spring MVC bug? I'm guessing no, because 3 path variables should be a pretty common scenario (would barely qualify for a corner case).

Update: This seems to be a problem with my HTTP client (chrome postman), not my server code. I was able to get the expected results when I send the same request via curl.

Update: Actually, the error has come back and is happening regardless of the client (postman, curl, etc). So it's definitely a server side problem. Here are the logs

01:35:39.409 [http-bio-8080-exec-5] DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'mvc-dispatcher' processing POST request for [/resourceA/1/resourceB/1/resourceC/1]
01:35:39.411 [http-bio-8080-exec-5] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /resourceA/1/resourceB/1/resourceC/1
01:35:39.414 [http-bio-8080-exec-5] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Returning handler method [public void com.sample.controller.DefaultController.unmappedRequest()]
01:35:39.414 [http-bio-8080-exec-5] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'defaultController'
01:35:39.422 [http-bio-8080-exec-5] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public void com.sample.controller.DefaultController.unmappedRequest()]: com.sample.exception.APIException: Url pattern is invalid.
01:35:39.423 [http-bio-8080-exec-5] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'globalControllerExceptionHandler'
01:35:39.423 [http-bio-8080-exec-5] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Invoking @ExceptionHandler method: public com.sample.model.ErrorInfo com.sample.controller.GlobalControllerExceptionHandler.handleAPIException(com.sample.exception.APIException)
01:35:39.460 [http-bio-8080-exec-5] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Written [com.sample.model.ErrorInfo@df27cd5] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@2ae18b1a]
01:35:39.460 [http-bio-8080-exec-5] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'mvc-dispatcher': assuming HandlerAdapter completed request handling
01:35:39.460 [http-bio-8080-exec-5] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request

Found what's causing the problem:
So after mucking around with many different things, I've found what was causing this problem. I have a DefaultController.java which was intended to catch all Urls that didn't match any of the other controllers and report a nice resource not found in my REST service's error response. The DefaultController has the following code:

package com.sample.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.sample.exception.APIException;
import com.sample.exception.APIException.Code;

@Controller
public class DefaultController {

    @RequestMapping("/**")
    public void unmappedRequest() {
        throw new APIException(Code.INVALID_URL_PATTERN,
            "There is no resource for this path");
    }
}

This has worked well for me. But in this case, somehow this DefaultController "/**" was picking my Url before the controller that was actually setup to receive "/resourceA/{resourceAId}/resourceB/{resourceBId}/resourceC/{resourceCId}". Removing DefaultController fixed the problem for me. Now my question is how can I retain DefaultController functionality without having it get triggered before my TenderController.

도움이 되었습니까?

해결책

I believe this is likely a Spring bug that will probably be fixed at some point. Ran into the exact same issue. In our case, we were able to get around this by replacing the Default Spring Controller that handled unmapped requests with a plain vanilla servlet to handle the 404s - details on how to do it are here.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top