Spring Social Facebook UncategorizedApiException:

2019-07-10 00:53发布

问题:

I have developed a simple web application that accesses Facebook data through Spring Boot (v1.2.6.RELEASE) and Spring Social Facebook (v2.0.2.RELEASE) similar to the example given here

I have created a new App in Facebook which use Graph API version 2.5.

According to this example i have modified facebookConnect.html putting this different scope to the method POST request:

<html>
<head>
    <title>Facebook Extractor</title>
</head>
<body>
    <h3>Connect to Facebook</h3>

    <form action="/recommender/connect/facebook" method="POST">
        <input type="hidden" name="scope" value="public_profile, user_friends, email, user_likes" />
        <div class="formInfo">
            <p>You aren't connected to Facebook yet. Click the button to connect this application with your Facebook account.</p>
        </div>
        <p><button type="submit">Connect to Facebook</button></p>
    </form>
</body>

handled by ConnectController which kick off the OAuth authorization code flow...

After OAuth success (with permission granted) and connection is done, i get the following error:

(#3) application does not have the capability to make this api call.

Here the main part of the controller which handle the redirect from Facebook App:

@Controller

@Scope(value="session") public class FacebookExtractor {

private Logger logger = Logger.getLogger(FacebookExtractor.class);

private Facebook facebook;
@Autowired
GraphDatabase graphDatabase;
@Autowired
PersonRepository personRepository;
@Autowired
UserProfileRepository userProfileRepository;
@Autowired
AttributeDefinitionRepository attributeDefinitionRepository;
@Autowired
AttributeRepository attributeRepository;
@Autowired
ConceptRepository conceptRepository;
@Autowired
RecommenderGovConsumerRepository recGovRepository;
@Autowired
GovConsumerInfluencedByGovConsumerRelationshipRepository influenceRepository;
@Autowired
ExtractorListener listener;

private String userBind;

@Inject
public FacebookExtractor(Facebook facebook) {
    this.facebook = facebook;
}

@RequestMapping(method = RequestMethod.GET, value = "/facebookExtractor")
public ModelAndView FacebookDataUserExtraction() {
    if (!facebook.isAuthorized()) {
        return new ModelAndView("redirect:/recommender/connect/facebook");
    }
    String account=userBind;
    Long netId = null;
    Transaction tx = graphDatabase.beginTx();
    try {
        User user = facebook.userOperations().getUserProfile();
        Person p = savePerson(user);
        UserProfile up = saveUserProfile(user);
        up.setUser(p);
        userProfileRepository.save(up);
        p.setProfile(up);
        saveSocialInteractions(up);
        saveSocialPreferences(up);
        personRepository.save(p);
        netId=p.getId();
        tx.success();
    } finally {
        tx.close();
    }
    if (account != null) {
        /*
         * Binding di Facebook riuscito.
         */
        listener.onProfileDataCompleted(netId, SNAccountType.Facebook);
        listener.onInteractionsCompleted(netId, InteractionType.Friendship, SNAccountType.Facebook);
        return new ModelAndView("redirect:http://localhost:8080/portal",
                "accountF", account);
    } else {
        //vista opportuna per estrazione avvenuta senza bind
        return new ModelAndView("facebookNoBindExtraction");
    }
}

Watching the stack trace error, seems i'm getting error at this line in controller:

User user = facebook.userOperations().getUserProfile();

but it is strange because I should have permission to get basic user profile data by default.

Note: the same code with an older App in Facebook which use Graph API version 2.3 works perfectly, but i need a new one for different purpose and can't force a new App in Facebook to use that version...

Here the stack error i get....

12:01:16.953 [http-nio-9080-exec-5] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.social.UncategorizedApiException: (#3) Application does not have the capability to make this API call.] with root cause org.springframework.social.UncategorizedApiException: (#3) Application does not have the capability to make this API call. at org.springframework.social.facebook.api.impl.FacebookErrorHandler.handleFacebookError(FacebookErrorHandler.java:91) ~[spring-social-facebook-2.0.2.RELEASE.jar:2.0.2.RELEASE] at org.springframework.social.facebook.api.impl.FacebookErrorHandler.handleError(FacebookErrorHandler.java:59) ~[spring-social-facebook-2.0.2.RELEASE.jar:2.0.2.RELEASE] at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:614) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:570) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:545) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:253) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.social.facebook.api.impl.FacebookTemplate.fetchObject(FacebookTemplate.java:214) ~[spring-social-facebook-2.0.2.RELEASE.jar:2.0.2.RELEASE] at org.springframework.social.facebook.api.impl.FacebookTemplate.fetchObject(FacebookTemplate.java:209) ~[spring-social-facebook-2.0.2.RELEASE.jar:2.0.2.RELEASE] at org.springframework.social.facebook.api.impl.UserTemplate.getUserProfile(UserTemplate.java:53) ~[spring-social-facebook-2.0.2.RELEASE.jar:2.0.2.RELEASE] at org.springframework.social.facebook.api.impl.UserTemplate.getUserProfile(UserTemplate.java:49) ~[spring-social-facebook-2.0.2.RELEASE.jar:2.0.2.RELEASE] at it.cerict.recommender.extractor.controllers.FacebookExtractor.FacebookDataUserExtraction(FacebookExtractor.java:89) ~[classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_60] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_60] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_60] at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_60] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.0.26.jar:8.0.26] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at it.cerict.recommender.config.CORSFilter.doFilter(CORSFilter.java:22) ~[classes/:na] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.1.7.RELEASE.jar:4.1.7.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) ~[tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1526) [tomcat-embed-core-8.0.26.jar:8.0.26] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1482) [tomcat-embed-core-8.0.26.jar:8.0.26] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_60] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_60] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.26.jar:8.0.26] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_60]

回答1:

The problem is with PROFILE_FILEDS in

public User getUserProfile(String facebookId) {
  return graphApi.fetchObject(facebookId, User.class, PROFILE_FIELDS);
}

Array PROFILE_FIELDS contains many many fields and as I understang correctly spring-social docs, Facebook returned only this fields you have rights to view. But now something changed:

$curl "https://graph.facebook.com/v2.5/me?access_token=[access_tokens]&fields=[all_the_fields_from_PROFILE_FIELDS]
{"error":{"message":"(#3) Application does not have the capability to make this API call.","type":"OAuthException","code":3,"fbtrace_id":"Antivssjj1d"}}

$ curl "https://graph.facebook.com/v2.3/me?access_token=[access_token]&fields=id,about"
{"id":"1135898619755433"}

In the meantime I've found sollution. Instead

User profile = facebook.userOperations().getUserProfile()

we can use

User profile = facebook.fetchObject("me", User.class, "id", "name", "link", "email");

//Note: facebook = org.springframework.social.facebook.api.Facebook
//Note: User.class = org.springframework.social.facebook.api.User.class


回答2:

Or wait the update of spring-social-facebook, there is a pull request pending on the repository: https://github.com/spring-projects/spring-social-facebook/pull/171

UPDATE: Fix for me with the 2.0.3.RELEASE version.



回答3:

I continued to get this error, even after adding the publish_actions scope that my app needed. The issue in my case was that I needed Facebook to review my app. This is the case if your app was created after April 30th, 2014 and one or more of the following is true:

  • You want your App to be listed in the Facebook App Center
  • You want to create new, or edit existing, Open Graph actions and stories
  • Your app asks people for any extended permissions including publish_actions

Have a look at the Facebook Login Review FAQs for more info on the review process.