尝试访问节点类型(用户)上不存在的字段(上下文)

Tried accessing nonexisting field (context) on node type (User)

我正在从 Connection<?> connection 访问 connection.fetchUserProfile(),但它给出了 org.springframework.social.UncategorizedApiException: (#100) Tried accessing nonexisting field (context) on node type (User)。这个错误以前从未发生过。

行家:

<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-facebook</artifactId>
     <version>3.0.0.M3</version>
</dependency>

知道为什么会这样吗?

我遇到了同样的问题,到处寻找解决方案。运气不好,我最终编写了一个调用 Facebook Graph API 并填充 UserProfile 对象的自定义服务。

向您的项目添加一个新的 FBService.java class:

package net.attacomsian.services;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.*;
import org.springframework.social.connect.UserProfile;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

@Service
public class FBService {

    private final RestTemplate restTemplate;

    public FBService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    public UserProfile getProfile(String id, String accessToken) {
        try {
            //params
            String params = "fields=id,name,email,first_name,last_name&access_token=" + accessToken;

            //build url
            String url = "https://graph.facebook.com/v3.2/" + id + "?" + params;

            //create headers
            HttpHeaders headers = new HttpHeaders();
            headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));

            // create request
            HttpEntity request = new HttpEntity(headers);

            //use rest template
            ResponseEntity<String> response = this.restTemplate.exchange(url, HttpMethod.GET, request, String.class);

            //check for status code
            if (response.getStatusCode().is2xxSuccessful()) {
                JsonNode root =  new ObjectMapper().readTree(response.getBody());

                // return a user profile object
                return new UserProfile(root.path("id").asText(), root.path("name").asText(), root.path("first_name").asText(),
                        root.path("last_name").asText(), root.path("email").asText(), null);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }
}

v3.2 是 Graph API 版本。用你自己的替换它。现在将此服务注入您的控制器,而不是调用:

UserProfile profile = connection.fetchUserProfile();

调用新服务 getProfile() 方法如下:

Profile profile = fbService.getProfile("me", connection.createData().getAccessToken());

它对我来说就像魔法一样。

更新: 这是完整的网络控制器代码,展示了如何在为 Google 的用户继续使用默认服务的同时使用 Facebook 的自定义服务简介:

@Controller
public class AuthController {

    private FBService fbService;
    private final ProviderSignInUtils providerSignInUtils;

    public AuthController(FBService fbService,
                          ConnectionFactoryLocator connectionFactoryLocator,
                          UsersConnectionRepository connectionRepository) {
        this.fbService = fbService;
        this.providerSignInUtils = new ProviderSignInUtils(connectionFactoryLocator, connectionRepository);
    }

    @GetMapping("/social-signup")
    public String socialSignup(WebRequest request, HttpSession session) {

        Connection<?> connection = providerSignInUtils.getConnectionFromSession(request);
        if (connection == null) {
            return "redirect:/login";
        }

        //fetch user information
        UserProfile profile = null;
        if (connection.getKey().getProviderId().equalsIgnoreCase("google")) {
            profile = connection.fetchUserProfile();
        } else if (connection.getKey().getProviderId().equalsIgnoreCase("facebook")) {
            profile = fbService.getProfile("me", connection.createData().getAccessToken());
        }

        // TODO: continue doing everything else
    }
}

编辑#2:我又遇到了这个问题。如果您想弄清楚如何修改某些包并将其添加到 maven,那么请继续阅读,但是如果您只是想弄清楚如何消除该错误(以及其他类似的错误,涉及不同的properties) 那么你应该只遵循这个答案中的建议: 它比我的好多了。


虽然 attacomsian 的解决方案可能对某些人有用,但我依赖抽象的 UserProfile 来提供其他服务,例如 Google 登录,他们的解决方案对我来说会破坏它。

我发现一个侵入性较小的操作是前往 ss-facebook's github 并按照他们的前两行说明从源代码构建:

git clone git://github.com/SpringSource/spring-social-facebook.git
cd spring-social-facebook

但在执行 运行 ./gradlew build 命令之前,请打开此文件进行编辑:

./spring-social-facebook/src⁩/main⁩/java⁩/org⁩/springframework⁩/social⁩/facebook⁩/api⁩/UserOperations.java

向下滚动到底部并从数组中删除“上下文”,更改

static final String[] PROFILE_FIELDS = {
        "id", "about", "age_range", "birthday", "context", "cover", "currency", "devices", "education", "email", 

        ....

        "website", "work"
    };

static final String[] PROFILE_FIELDS = {
        "id", "about", "age_range", "birthday", "cover", "currency", "devices", "education", "email", 

        ....

        "website", "work"
    };

现在回到源文件夹和运行最后的命令:

./gradlew build

你会得到一个 ./build 文件夹,里面有一个 zip 文件,解压它找到你的工作 jar!我使用 maven,并将我拥有的两个远程依赖项替换为一些本地依赖项,如下所示:

        <dependency>
            <groupId>org.springframework.social</groupId>
            <artifactId>spring-social-facebook</artifactId>
            <version>3.0.0.M1</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/spring-social-facebook-3.0.0.BUILD-SNAPSHOT.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.springframework.social</groupId>
            <artifactId>spring-social-facebook-web</artifactId>
            <version>3.0.0.M1</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/spring-social-facebook-web-3.0.0.BUILD-SNAPSHOT.jar</systemPath>
        </dependency>

编辑: 从那以后我了解到这不是将像这样的自定义 jar 包含在 Maven 中的最佳方式。将它实际放入其中就像 pom 中的其他罐子一样工作,这有点困难。我最终通过 运行 手动安装我制作的两个 jar 的这些命令使它更好地工作(3.0.1337 是我为避免名称冲突而编写的版本 - 不确定是否有必要)...

mvn org.apache.maven.plugins:maven-install-plugin:2.3.1:install-file -Dfile=web/src/main/resources/lib/spring-social-facebook-3.0.1337.BUILD-SNAPSHOT.jar -DgroupId=org.springframework.social -DartifactId=spring-social-facebook -Dversion=3.0.1337 -Dpackaging=jar -DlocalRepositoryPath=web/src/main/resources/local-repo -DgeneratePom=true

mvn org.apache.maven.plugins:maven-install-plugin:2.3.1:install-file -Dfile=web/src/main/resources/lib/spring-social-facebook-web-3.0.1337.BUILD-SNAPSHOT.jar -DgroupId=org.springframework.social -DartifactId=spring-social-facebook-web -Dversion=3.0.1337 -Dpackaging=jar -DlocalRepositoryPath=web/src/main/resources/local-repo -DgeneratePom=true

...进入我在 pom 中声明的本地存储库,如下所示:

<repositories>
  <repository>
    <id>local-repo</id>
    <url>file://${project.basedir}/src/main/resources/local-repo</url>
  </repository>
</repositories>

然后我将 jars 添加到源代码管理并在我的 gitignore 中添加一行以忽略 /src/main/resources/local-repo 文件夹。

如果有人有更好的方法来保留所有原始代码,只从该文件末尾的数组中删除该“上下文”字符串,并希望避免所有这些垃圾,而有利于简单的单数 class 替换或扩展,请插话。我只是包括所有这些,因为它肯定会节省我一天的时间,而不必一一解决这些问题。

免责声明: 我不知道这可能有什么副作用,而且我不熟悉这个库。尽管我怀疑这会造成很大的伤害,并且显然是这一领域的改进,但如果不花更多时间在这个库上工作,我可能无法预见到这种变化可能会产生影响。因此,我不提供修改后的 JAR,只是为其他人写下一些简单的说明。继续执行这些说明需要您自担风险。

此外,我刚刚检查了许可证,如果您确实进行了此更改,请确保您遵循以下说明:

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

如果我遗漏了任何其他法律内容,请发表评论让我知道。

在回答@JohnTD 时,如果您使用多个社交关系,那么您只需执行以下操作即可合并@attacomsian 的回答

   public void getProfile(Connection connection) {
    UserProfile socialMediaProfile = getSocialUserProfile(connection);
      .
      .
}


/**
 * Checks for facebook connection requests so that we can call a custom service
 *
 * @param connection
 * @return
 */
private UserProfile getSocialUserProfile(Connection<?> connection) {
    if (isFacebookConnection(connection)) {
        return facebookService.getProfile("me", connection.createData().getAccessToken());
    } else {
        return connection.fetchUserProfile();
    }
}

private boolean isFacebookConnection(Connection<?> connection) {
    return connection.getApi().toString().indexOf("FacebookTemplate") != -1;
}

看来facebook social不会再更新了

反正我不想花时间换库。

我刚刚获取了我需要的字段:

FacebookTemplate facebook = new FacebookTemplate(socialToken);

User facebookUser = facebook.fetchObject(FacebookUtils.LOGGED_USER_ID, User.class, FacebookUtils.USER_FIELD_ID, FacebookUtils.USER_FIELD_EMAIL,
            FacebookUtils.USER_FIELD_FIRST_NAME, FacebookUtils.USER_FIELD_LAST_NAME);

我添加到我的 FacebookUtils 中的字段:

/** The Constant LOGGED_USER_ID. */
public static final String LOGGED_USER_ID = "me";

/** The Constant USER_FIELD_ID. */
public static final String USER_FIELD_ID = "id";

/** The Constant USER_FIELD_EMAIL. */
public static final String USER_FIELD_EMAIL = "email";

/** The Constant USER_FIELD_FIRST_NAME. */
public static final String USER_FIELD_FIRST_NAME = "first_name";

/** The Constant USER_FIELD_LAST_NAME. */
public static final String USER_FIELD_LAST_NAME = "last_name";

我通过用任何其他字段覆盖缺失的字段来解决它。在您的代码中的某处添加:

    // Patch: spring social facebook tries to get a removed "context" field
    for (int i=0; i<UserOperations.PROFILE_FIELDS.length; i++) {

        // To avoid error override with existing "ratings" field
        if (UserOperations.PROFILE_FIELDS[i].equals("context")) {
            UserOperations.PROFILE_FIELDS[i] = "ratings";
        }
    }