Fred Lintz
Fred Lintz

Reputation: 331

Feign Client GET request, throws "Method Not Allowed: Request method 'POST' not supported" from microservice

I have a Vue.js app with a spring backend, which makes calls via a Feign Client to an 'orders' microservice. I have many GET requests that are working fine using @PathVariable(s), but I have 1 GET request which can have any number of different URL params passed into it, and have defined an OrderListItemParameters class and have added it to the GET request, to pass the params to the microservice. I can see that the request is making it to the microservice, but it is getting denied with a "org.zalando.problem.spring.common.AdviceTrait : Method Not Allowed: Request method 'POST' not supported" error.

Ive made sure the OrderListItemParameters being sent by the feign client matches the OrderListItemParameters expected at the microservice. And there is no mention of POST in either the Feign client, or the microservice. I only have GET, and PUT endpoints.

Java BackEnd for FrontEnd Code:

@RestController
@RequestMapping("/api")
public class OrdersResource {

    ## THIS WORKS NO PROBLEM
    @GetMapping("/order/{orderId}")
    public @ResponseBody
    ResponseEntity<?> order(@PathVariable int orderId) throws Exception {
        return ResponseEntity.ok(orderClient.order(orderId));
    }


    ## THIS ENDPOINT IS THE ONE THAT FAILS
    @GetMapping("/order/list")
    public @ResponseBody
    ResponseEntity<?> orders(OrderListItemParameters orderListItemParams) {
        return ResponseEntity
           .ok(orderClient.getOrderList(orderListItemParams));
    }
}

@Component
@AuthorizedUserFeignClient(name = "orders")
public interface OrderClient {

    ## THIS WORKS NO PROBLEM
    @RequestMapping(method = RequestMethod.GET, value = "/api/order/pg/{orderId}")
    String order(@PathVariable("orderId") int orderId);

    ## THIS ENDPOINT IS THE ONE THAT FAILS
    @RequestMapping(method = RequestMethod.GET, value = "/api/order/list")
    String getOrderList(OrderListItemParameters orderListItemParams);
}

OrderListItemParameters Class passed in as URL params: (this is identical in the microservice)

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class OrderListItemParameters implements Serializable {

  private String searchPhrase;
  private String startDate;
  private String endDate;
  private ArrayList<String> sortKey;
  private ArrayList<String> sortValue;
  private Integer from;
  private Integer size;
}

(with getters, and setters) 

Microservice Controller:

@Controller
@RequestMapping("/api")
public class OrderController {

  ## THIS WORKS NO PROBLEM
  @GetMapping("/order/pg/{orderId}")
  public @ResponseBody
  Order pgorder(@PathVariable int orderId) {
    return orderService.getPgOrderById(orderId);
  }

  ## THIS ENDPOINT IS THE ONE THAT FAILS
  @GetMapping("/order/list")
  public @ResponseBody
  ResponseEntity getOrderListItemsByMerchant(OrderListItemParameters orderListItemParams) {
    return orderService
        .getOrderListItemsByMerchant(orderListItemParams);
}

Here's a pic of the logs: Clearly Stating GET requests... Logs

Any help figuring out why the controller thinks this is a POST request would be awesome.

Upvotes: 3

Views: 14331

Answers (3)

gbarco
gbarco

Reputation: 149

The problem should be solved by changing:

String getOrderList(OrderListItemParameters orderListItemParams);

To:

String getOrderList(@SpringQueryMap OrderListItemParameters orderListItemParams);

The problem is that using a data class with no parameters maps it to a body request which GET requests cannot encode; thus converting the request to a POST in the client, which fails with a 405 on a correct server that does not implement the verb.

In plain Feign, as opposed to Spring integration @QueryMap should be used. More detailed information can be found here

Upvotes: 3

Pivoter
Pivoter

Reputation: 419

Presumably, the controller considers your method as POST due to the presence of the request body.

I would try two options:

@GetMapping("/order/list")
public @ResponseBody ResponseEntity<?> getOrderListItemsByMerchant() {
    // method code
}

or

@PostMapping("/order/list")
public @ResponseBody ResponseEntity<?> getOrderListItemsByMerchant(OrderListItemParameters orderListItemParams) {
    // method code
}

UPDATE. If you still want to use the GET method with body, you need to change the method call in feign client by explicitly marking the parameter:

@Component
@AuthorizedUserFeignClient(name = "orders")
public interface OrderClient {

    // feign methods

    @RequestMapping(method = RequestMethod.GET, value = "/api/order/list")
    @Headers(value = "Content-Type: application/json")
    ResponseEntity<String> getOrderList(@RequestParam("orderListItemParams") 
    OrderListItemParameters orderListItemParams);

}

Upvotes: 2

Fred Lintz
Fred Lintz

Reputation: 331

Here's the solution that ended up working...

#Resource
@GetMapping("/order/list")
public @ResponseBody 
ResponseEntity<?> getOrderListItemsByMerchant(
    @RequestParam Map<String, Object> params) {
    // method code
}

#Feign Client
@RequestMapping(method = RequestMethod.GET, value = "/api/order/list")
ResponseEntity<String> getOrderList(@RequestParam Map<String, Object> params);

#Microservice Controller
@GetMapping("/order/list")
public @ResponseBody
ResponseEntity getOrderListItemsByMerchant(OrderListItemParameters orderListItemParams) {
    // method code
}

This allows me to fetch a URL with dynamic params, and the microservice still sees the request as a GET.

It would be nice to have the same defined object on either side of the request, but I was unable to make @SpringQueryMap or @QueryMap work at all.

I hate Feign.

Upvotes: 5

Related Questions