尝试访问节点类型(用户)上不存在的字段(上下文)
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";
}
}
我正在从 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";
}
}