Search code examples
springspring-bootspring-securityresttemplatespring-hateoas

Field restTemplate required a single bean, but 2 were found


I am trying to resolve an issue with this code:

import io.christdoes.wealth.tracker.controller.error.ReCaptchaInvalidException;
import io.christdoes.wealth.tracker.controller.error.ReCaptchaUnavailableException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.regex.Pattern;

@Service("captchaService")
public class CaptchaService implements ICaptchaService {
    private final static Logger LOGGER = LoggerFactory.getLogger(CaptchaService.class);

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private CaptchaSettings captchaSettings;

    @Autowired
    private ReCaptchaAttemptService reCaptchaAttemptService;

    @Autowired
    private RestOperations restTemplate;

    private static final Pattern RESPONSE_PATTERN = Pattern.compile("[A-Za-z0-9_-]+");

    @Override
    public void processResponse(final String response) {
        LOGGER.debug("Attempting to validate response {}", response);

        if (reCaptchaAttemptService.isBlocked(getClientIP())) {
            throw new ReCaptchaInvalidException("Client exceeded maximum number of failed attempts");
        }

        if (!responseSanityCheck(response)) {
            throw new ReCaptchaInvalidException("Response contains invalid characters");
        }

        final URI verifyUri = URI.create(String.format("https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s&remoteip=%s", getReCaptchaSecret(), response, getClientIP()));
        try {
            final GoogleResponse googleResponse = restTemplate.getForObject(verifyUri, GoogleResponse.class);
            LOGGER.debug("Google's response: {} ", googleResponse.toString());

            if (!googleResponse.isSuccess()) {
                if (googleResponse.hasClientError()) {
                    reCaptchaAttemptService.reCaptchaFailed(getClientIP());
                }
                throw new ReCaptchaInvalidException("reCaptcha was not successfully validated");
            }
        } catch (RestClientException rce) {
            throw new ReCaptchaUnavailableException("Registration unavailable at this time.  Please try again later.", rce);
        }
        reCaptchaAttemptService.reCaptchaSucceeded(getClientIP());
    }

    private boolean responseSanityCheck(final String response) {
        return StringUtils.hasLength(response) && RESPONSE_PATTERN.matcher(response).matches();
    }

    @Override
    public String getReCaptchaSite() {
        return captchaSettings.getSite();
    }

    @Override
    public String getReCaptchaSecret() {
        return captchaSettings.getSecret();
    }

    private String getClientIP() {
        final String xfHeader = request.getHeader("X-Forwarded-For");
        if (xfHeader == null) {
            return request.getRemoteAddr();
        }
        return xfHeader.split(",")[0];
    }
}

Error

web - 2019-09-25 14:20:18,416 [restartedMain] INFO  o.a.c.c.StandardService - Stopping service [Tomcat]
web - 2019-09-25 14:20:18,491 [restartedMain] ERROR o.s.b.d.LoggingFailureAnalysisReporter - 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field restTemplate in io.christdoes.wealth.tracker.captcha.CaptchaService required a single bean, but 2 were found:
    - org.springframework.hateoas.config.ConverterRegisteringWebMvcConfigurer#0: defined in null
    - org.springframework.hateoas.config.ConverterRegisteringWebMvcConfigurer#1: defined in null


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed


Process finished with exit code 0

I have been trying to resolve this issue and could not find a way to resolve it. I kept on getting the above error.

I have searched both SO and Github where similar errors were reported but non helped. Someone noted that it's a dependency issue, that's having both spring-web, and hateoas could be the issue. I removed the web yet the problem persists.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Though an earlier project with spring-boot dependencies of version 2.0.4 worked, I am presently using the latest version which is 2.1.8. I will not like to revert to the earlier version because I have recent codes that I solely used 2.1.8.

What am I to do to get past this challenge?


Solution

  • You can check one of those method :

    1. The first :

      is to remove the spring boot , because it's a hierarchic dependency of spring-boot-starter-hateoas

      in which I think this cause twice creation of the bean

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      

      then clean , and reinstall your project . run again .

    2. Second Solution :

      create your own RestTemplate bean , since this last implement the RestOperations interface : and use it as folow :

      In a config class create a bean :

      @Bean
      public RestTemplate myRestTemplate(RestTemplateBuilder builder) {
      
          return builder
                   .setConnectTimeout(10000)
                   .setReadTimeout(10000)
                   .build();
      }
      

      then replace the autowired

      @Autowired
      private RestOperations restTemplate; 
      

      by :

      @Autowired
      private RestTemplate myRestTemplate;