Spring Framework и RestTemplate: невозможность использования службы REST

Прежде всего, у меня есть две службы Spring REST: служба A и служба B. Служба A должна использовать некоторые методы, предоставленные службой B. Оба, A и B, являются Spring @RestController.

Служба A имеет метод POST:

    @RequestMapping(value = "/mediumcandy/linkreachable", method = RequestMethod.POST)
    public ResponseEntity<ShortURL> shortenerIfReachable(@RequestParam("url") String url,
            @RequestParam(value = "sponsor", required = false) String sponsor,
            @RequestParam(value = "brand", required = false) String brand,
            HttpServletRequest request) {
        ShortURL su = null;

        /*************************************************************
         * CONSUMING REST Service
         *************************************************************/
        Map<String, String> vars = new HashMap<String, String>();
        vars.put("url", url);

        RestTemplate restTemplate = new RestTemplate();
        su = restTemplate.postForObject("http://SERVICE_URI_HERE/linkreachable", null, ShortURL.class, vars);
        /****************************************************************/

        if (su != null) {
            HttpHeaders h = new HttpHeaders();
            h.setLocation(su.getUri());
            return new ResponseEntity<>(su, h, HttpStatus.CREATED);
        } else {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }
    }

А метод POST в Service B, вызываемый Service A с помощью Spring RestTemplate, выглядит следующим образом:

@RequestMapping(value = "/linkreachable", method = RequestMethod.POST)
public ShortURL shortenerIfReachable(@RequestParam("url") String url,
        @RequestParam(value = "sponsor", required = false) String sponsor,
        @RequestParam(value = "brand", required = false) String brand,
        HttpServletRequest request) {
    ShortURL su = null;
    boolean isReachableUrl = ping(url);

    if (isReachableUrl){
        su = createAndSaveIfValid(url, sponsor, brand, UUID
                .randomUUID().toString(), extractIP(request));
        System.out.println("URL REACHABLE!");
    }

    return su;
}

Метод RestTemplate postForObject() в Service A доставляет мне проблемы, всегда выдавая HttpClientErrorException: 400 Bad Request:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw
exception [Request processing failed; nested exception is org.springframework.web.client.H
ttpClientErrorException: 400 Bad Request] with root cause

org.springframework.web.client.HttpClientErrorException: 400 Bad Request
        at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultR
esponseErrorHandler.java:91)
        at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.ja
va:615)
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:573)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:537)
        at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:339
)
        at urlshortener2014.mediumcandy.web.MediumCandyController.shortenerIfReachable(Med
iumCandyController.java:39)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.ja
va:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(Invocabl
eHandlerMethod.java:221)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(
InvocableHandlerMethod.java:137)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMe
thod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdap
ter.invokeHandleMethod(RequestMappingHandlerAdapter.java:777)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdap
ter.handleInternal(RequestMappingHandlerAdapter.java:706)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(
AbstractHandlerMethodAdapter.java:85)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:94
... etc

Я следил за некоторыми учебными пособиями Spring, чтобы создать свой сервис, а также проверил API-документы Spring Framework, но мне не удалось заставить его работать.


person charliebrownie    schedule 04.01.2015    source источник
comment
Не могли бы вы опубликовать файл web.xml и подтвердить, что при индивидуальном тестировании службы B (не вызывая службу A) она работает должным образом?   -  person liorsolomon    schedule 05.01.2015
comment
@liorsolomon да, служба B по отдельности работает нормально. Я не использую web.xml, поскольку я не развертываю внешний экземпляр. Я сделал это так, как описано здесь: spring.io/guides/gs/rest-service   -  person charliebrownie    schedule 05.01.2015
comment
@liorsolomon вот репозиторий проекта: github.com /charliemc/UrlShortener2014/tree/developer/ MediumCandyController — это служба A, а UrlShortenerControllerWithLogs — это служба B   -  person charliebrownie    schedule 05.01.2015
comment
меня устраивает. Я не смог воспроизвести проблему. не могли бы вы объяснить, как вы тестируете сервис?   -  person liorsolomon    schedule 05.01.2015
comment
@liorsolomon Это так? Я использую curl следующим образом: curl -v -d "url=http://www.stackoverflow.com/" -X POST http://localhost:8080/mediumcandy/linkreachable и он продолжает отвечать Bad Request (сервер также выводит исключение)   -  person charliebrownie    schedule 05.01.2015
comment
@liorsolomon Я только что попробовал протестировать его с помощью SoapUI (http://localhost:8080/mediumcandy/linkreachable?url=http://www.stackoverflow.com) и продолжаю получать тот же результат.   -  person charliebrownie    schedule 05.01.2015


Ответы (3)


Используйте Spring HATEOAS, чтобы решить эту проблему! Учитывая ServiceA и ServiceB, в ServiceA используйте Spring HATEOAS для получения URL-адреса времени выполнения контроллера/метода в «ServiceB» следующим образом:

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
...
String restURI = linkTo(methodOn(ServiceB.class).
  shortenerIfReachable(null, null, null, null)).toString();
RestTemplate restTemplate = new RestTemplate();
su = restTemplate.postForObject(restURI, null, ShortURL.class, vars);
person Francisco J. Lopez-Pellicer    schedule 05.01.2015
comment
У меня проблемы с methodOn(): компилятор говорит, что он не определен для типа Class‹ServiceB› - person charliebrownie; 05.01.2015
comment
Когда я говорил @liorsolomon, я был полностью уверен, что Eclipse IDE выполняет этот импорт автоматически. Проверил, теперь компилируется нормально! - person charliebrownie; 05.01.2015
comment
Затем отметьте этот ответ как решение вашего запроса о помощи XD - person Francisco J. Lopez-Pellicer; 05.01.2015

@Francisco ты был почти там

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
...
String restURI = linkTo(methodOn(UrlShortenerControllerWithLogs.class).
                shortenerIfReachable(url, null, null, null)).toString();

RestTemplate restTemplate = new RestTemplate();

su = restTemplate.postForObject(restURI, null, ShortURL.class);

Вы должны передать параметр url при вызове shortenerIfReachable, и я также удалил vars из вызова postForObject.

person liorsolomon    schedule 05.01.2015
comment
У меня все еще есть проблемы с этим методом: methodOn() Компилятор продолжает говорить, что он не определен для типа Class‹ServiceB› - person charliebrownie; 05.01.2015
comment
Ой, извини! Я был полностью уверен, что Eclipse IDE выполняет этот импорт автоматически. Проверил, и теперь работает нормально! - person charliebrownie; 05.01.2015

Попробуйте следующий URL: curl --data "url=http://www.stackoverflow.com" http://localhost:8080/linkreachable

person liorsolomon    schedule 05.01.2015
comment
Такой способ работает нормально, но при этом не используется Service A. Мне нужно вызвать этот метод /linkreachable в Service B из метода /mediumcandy/linkreachable Service A (я знаю, что это может звучать немного глупо, потому что результат точно такой же, но я надо сделать так). - person charliebrownie; 05.01.2015
comment
просто представьте, что Service A — это клиент Java, который использует REST API (Service B), это то, что мне нужно. - person charliebrownie; 05.01.2015