【问题标题】:HTTP 302 when Google Drive API sends push notificationsGoogle Drive API 发送推送通知时的 HTTP 302
【发布时间】:2017-05-31 04:37:50
【问题描述】:

我刚刚设法调用 Google Drive API 来启用文件的推送通知。

设置推送通知的代码如下所示:

public class SampleServlet extends AbstractAppEngineAuthorizationCodeServlet {
    private final static Logger logger = Logger.getLogger(SampleServlet.class.getName());
    private static final long serialVersionUID = 1L;
    // Constants omitted

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException, ServletException {

        AuthorizationCodeFlow authFlow = initializeFlow();
        Credential credential = authFlow.loadCredential(getUserId(req));

        StringBuilder resultFromWatch = new StringBuilder();
        Drive drive = new Drive.Builder(Utils.HTTP_TRANSPORT, Utils.JSON_FACTORY, credential).setApplicationName("t").build();

        try {
            Optional<Channel> channel = watchFile(drive, FILE_ID, CHANNEL_ID, "web_hook", "https://mydomain.appspot.com/drive");
            String channelStringTmp;
            if (channel.isPresent()) {
                channelStringTmp = channel.get().toString();
            } else {
                channelStringTmp = "null...";
            }
            resultFromWatch.append(channelStringTmp);
        } catch (Exception e) {
            resultFromWatch.append(e.getMessage());
        }

        final UserService userService = UserServiceFactory.getUserService();
        final String thisUrl = req.getRequestURI();
        // Send the results as the response
        PrintWriter respWriter = resp.getWriter();
        resp.setStatus(200);
        resp.setContentType("text/html");

        addLoginLogoutButtons(req, resp, resultFromWatch, userService, thisUrl, respWriter);
    }

    private static Optional<Channel> watchFile(Drive service, String fileId,
                                               String channelId, String channelType, String channelAddress) throws IOException {
        final Channel returnValue;
        Channel channel = new Channel();
        channel.setId(channelId);
        channel.setType(channelType);
        channel.setAddress(channelAddress);
        Drive.Files tmp = service.files();
        returnValue = tmp.watch(fileId, channel).execute();
        return Optional.fromNullable(returnValue);
    }

    @Override
    protected AuthorizationCodeFlow initializeFlow() throws ServletException, IOException {
        return Utils.initializeFlow();
    }

    @Override
    protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
        return Utils.getRedirectUri(req);
    }
}

从我的浏览器调用 servlet doGet 并登录后,我得到以下响应:

{
  "expiration": "1484565747000",
  "id": SAME_ID_AS_DEFINED_IN_SERVLET,
  "kind": "api#channel",
  "resourceId": A_NEW_ID,
  "resourceUri": "https:\/\/www.googleapis.com\/drive\/v3\/files\/FILE_ID?acknowledgeAbuse=false&alt=json"
}

下一步是定义当文件被修改时接收通知的控制器。看起来像这样:

@RestController
@RequestMapping("/drive")
public class ConcreteFileWatchController implements FileWatchController {
    private final static Logger logger = Logger.getLogger(ConcreteFileWatchController.class.getName());

    @RequestMapping(method = RequestMethod.POST)
    @ResponseStatus(value = HttpStatus.OK)
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        logger.info("Received watch call");
    }
}

最后我尝试在部署应用程序后修改文件(它部署在 Google App Engine 上),在查看 GAE 中的日志后,我可以看到有一个调用。但是,我的接收方法没有执行。我只在日志中看到一个 302 并附有一个大 json。除了我没有注意到我的方法被调用之外,真的看不到任何错误。该路径甚至在日志中看起来是正确的。我可能做错了什么?

有关错误的详细信息:

当我转到 Google Cloud 中的日志页面时,我看到了这条 302 消息:

11:34:35.957
POST
302
0 B
22 ms
APIs-Google; (+https://developers.google.com/webmasters/APIs-Google.html)
/drive
 10.72.94.97 - - [16/Jan/2017:11:34:35 +0100] "POST /drive HTTP/1.1" 302 - - "APIs-Google; (+https://developers.google.com/webmasters/APIs-Google.html)" "mydomain.appspot.com" ms=22 cpu_ms=0 cpm_usd=0 loading_request=0 instance=- app_engine_release=1.9.48 trace_id=d0e888dd3989e353344e40e41758fdf4

还有一个类似这样的json:

{
  protoPayload: {
    @
  type: "type.googleapis.com/google.appengine.logging.v1.RequestLog"
  appId: "p~blabla"
  versionId: "201t113050"
  requestId: "587ca1bb00ff05706f727465726261636b656e640001323031373031313674313133303530000100"
  ip: "10.76.94.97"
  startTime: "2017-01-16T10:34:35.957904Z"
  endTime: "2017-01-16T10:34:35.980366Z"
  latency: "0.022462s"
  method: "POST"
  resource: "/drive"
  httpVersion: "HTTP/1.1"
  status: 302
  userAgent: "APIs-Google; (+https://developers.google.com/webmasters/APIs-Google.html)"
  host: "blabla.appspot.com"
  instanceIndex: -1
  finished: true
  appEngineRelease: "1.9.48"
  traceId: "d0e888dd390f41758fdf4"
  first: true
}
insertId: "587cf6df9ded23f7"
httpRequest: {
status: 302
}
resource: {
type: "gae_app"
labels: {…}
}
timestamp: "2017-01-16T10:34:35.957904Z"
labels: {
appengine.googleapis.com/version_id: "2017013050"
clone_id: ""
appengine.googleapis.com/clone_id: ""
appengine.googleapis.com/module_id: "default"
version_id: "20170116t113050"
request_id: "587ca1bb00ff0e9dd0f39f31350001707e6561737974696d657265706f721373031313674313133303530000100"
appengine.googleapis.com/request_id: "587ca1bb00ff0e9dd0f39f31350001707e6561737974696d6572653674313133303530000100"
module_id: "default"
}
logName: "projects/blabla/logs/appengine.googleapis.com%2Frequest_log"
operation: {
id: "587ca1bb00ff0e9dde640001323031373031313674313133303530000100"
producer: "appengine.googleapis.com/request_id"
first: true
last: true
}
}

web.xml:

<web-app>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
    </context-param>
    <filter>
        <filter-name>CORS</filter-name>
        <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CORS</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>PlusBasicServlet</servlet-name>
        <servlet-class>packagename.PlusBasicServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>PlusBasicServlet</servlet-name>
        <url-pattern>/plusbasicservlet</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>PlusSampleServlet</servlet-name>
        <servlet-class>packagename.PlusSampleServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>PlusSampleServlet</servlet-name>
        <url-pattern>/plussampleservlet</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>FileWatchTestServlet</servlet-name>
        <servlet-class>packagename.ConcreteFileWatchController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FileWatchTestServlet</servlet-name>
        <url-pattern>/drive</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>PlusSampleAuthCallbackServlet</servlet-name>
        <servlet-class>packagename.PlusSampleAuthCallbackServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>PlusSampleAuthCallbackServlet</servlet-name>
        <url-pattern>/oauth2callback</url-pattern>
    </servlet-mapping>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>any</web-resource-name>
            <url-pattern>/plussampleservlet</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>
</web-app>

日志截图:

【问题讨论】:

  • 可以分享302错误的描述吗?
  • 我在云日志中添加了我能找到的所有数据。
  • HTTP 302 是 URL 重定向的代码,如果我没记错的话。可能是您需要点击您被重定向到的网址..
  • @RequestMapping 中使用的/*PUSH_URL*/ 是什么? (您可以跳过域名)。如果您尝试手动访问 URL(浏览器/curl),您会得到什么?另外,您确定应该在您的FileWatchController 中覆盖doGet 而不是doPost
  • 我检查了 PUSH_URL 并且它不正确。我纠正了它,但我得到了相同的结果。似乎 302 发生在我没有公开 REST 服务时。如果我禁用安全性,那么我可以“卷曲”到 FileWatchController。仅供参考,该方法的名称是 doGet ,这很糟糕,但我已经验证了注释确保它是一个 POST。我正在考虑这是否与安全相关。我添加了我的 web.xml,它看起来很奇怪吗?

标签: java spring-mvc google-app-engine google-drive-api


【解决方案1】:

由于您的回调是通过 Spring MVC 提供的,因此请确保您没有在其他地方配置任何隐藏的重定向。您应该首先使用常规 servlet 测试回调:

public class FileWatchTestServlet extends HttpServlet {
    private final static Logger logger = Logger.getLogger(FileWatchTestServlet.class.getSimpleName());

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        logger.info("Received watch call");
    }
}

在 web.xml 中:

<servlet>
    <servlet-name>FileWatchTestServlet</servlet-name>
    <servlet-class>com.mydomain.FileWatchTestServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>FileWatchTestServlet</servlet-name>
    <url-pattern>/drive</url-pattern>
</servlet-mapping>

如果 302 消失,则问题出在 Spring MVC 的调度程序上,因此您应该在那里查找可能导致重定向的任何内容(例如,在您的视图中)。

【讨论】:

  • 感谢您的建议。仍然得到 302。从日志中发布屏幕截图。我认为重定向是我的安全配置,将我重定向到谷歌用户身份验证。不过不知道怎么解决。
  • 您的安全约束似乎只适用于“/rest/plussampleservlet”。实际上,从plus-appengine-sample 复制的样子看起来有一些额外的内容,所以你应该删除它。如果您已经拥有,请发布您更新后的 web.xml,其中仅包含与您的应用相关的配置。
  • 另外,验证您的假设的另一种方法是简单地测试整个 部分注释掉。
猜你喜欢
  • 1970-01-01
  • 2017-05-13
  • 2016-07-31
  • 1970-01-01
  • 2013-07-21
  • 2018-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多