【问题标题】:How to modify HTTP request headers from an Android app?如何从 Android 应用修改 HTTP 请求标头?
【发布时间】:2014-06-23 12:16:19
【问题描述】:

有没有办法让我捕获从我的应用程序发送的所有 HTTP 请求,并在发送之前修改它们的标头?我想修改他们的 referer 标头,以便将请求发送到的服务器认为它们来自 Web 浏览器而不是移动应用程序。谢谢!

更新:为了给你更多背景信息,我计划使用 Phonegap 将 chrome 扩展 Instant Music 移植到 Android 应用程序中。一些允许在 PC 上使用的 YouTube 视频在移动设备上是不允许的,我怀疑这是因为嵌入在 android 应用程序中的 youtube 播放器没有引荐来源网址。我正在尝试找到解决此问题的方法,以便我也可以在移动设备上播放此类视频。

【问题讨论】:

  • 肮脏的黑客可以吗?还是您在寻找干净的解决方案?
  • @MaximusS 您是否有权访问库的源代码(至少通过反编译)?一个可行的方法是找到DefaultHttpClient 的初始化位置,然后在其中添加HttpRequestInterceptor(必要时通过反射)。但是,如果不知道它是哪个库,就很难知道确切的步骤(或者即使可能)。
  • 该库实际上是 Youtube 的 IFrame 播放器 API,它向 YouTube 服务器发送请求以获取视频数据。

标签: android http


【解决方案1】:

Youtube 检测到用户浏览器的Agent String,其中包含有关浏览器的信息。如果您要使用 WebView 来显示 youtube 视频,则可以设置 WebViewAgent String。 您可以在互联网上找到不同浏览器的代理字符串。我在这里找到了一些:Agent Strings

以下是我通过模拟 Firefox 浏览器在手机上播放 Bob Marley 的歌曲的方法:

package com.my.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

    public class MyActivity extends Activity {

        private WebView mWebView ;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            mWebView  = new WebView(this);
            // Enable javascript
            mWebView.getSettings().setJavaScriptEnabled(true);
            // Impersonate Mozzila browser
            mWebView.getSettings().setUserAgentString("Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:21.0.0) Gecko/20121011 Firefox/21.0.0");
            final Activity activity = this;

            mWebView.setWebViewClient(new WebViewClient() {
                public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                    Toast.makeText(activity, description, Toast.LENGTH_SHORT).show();
                }
            });

            mWebView .loadUrl("http://youtube.com/watch?v=x59kS2AOrGM");
            setContentView(mWebView);
        }

    }

编辑:

您还需要通过将此行添加到您的AndroidManifest.xml 来授予您的活动使用互联网的权限:

<uses-permission android:name="android.permission.INTERNET" /> 

【讨论】:

    【解决方案2】:

    您可以像这样轻松设置标题:

                final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36";
                final String URL = "http://www.cnn.com";
                final String REFERER_URL = "http://www.w3.org/hypertext/DataSources/Overview.html";
    
                HttpClient httpclient = new DefaultHttpClient();
                HttpGet request = new HttpGet(URL);
    
                // add request header
                request.addHeader("User-Agent", USER_AGENT);
                request.addHeader("Referer", REFERER_URL);
    
                try {
                    HttpResponse response = httpclient.execute(request);
                } catch (Exception e) {
                    // ...
                }
    

    我还添加了“User-Agent”,因为大多数网站使用它而不是“Referer”来确定客户端是否是移动浏览器。

    请注意,“Referer”拼写错误;这是我的代码故意的。见this link。猜想这是多年前的一个错字,只是卡住了。

    【讨论】:

    • 问题是我自己没有提出请求对象。这些请求是从我的应用程序使用的外部 API 发送的,因此我必须监听所有传出的 HTTP 请求并在发送它们之前修改它们的标头。这可以通过 chrome app/extensions 中的chrome.webRequest.beforeSend(function()..) 来完成,但我想知道如何在 Android 中实现相同的功能 :)
    • 简单地说,你不能。您唯一的选择是创建一个中间人 HTTP 代理,这在移动设备上并不是一个好主意。
    • 我想相信一定有办法?也许使用BroadcastReceiver
    • 你考虑过HttpRequestInterceptor的用法吗 - (developer.android.com/reference/org/apache/http/…)
    • @Vino 如果我没有request 可以具体引用的对象,这种方法似乎是不可能的。
    【解决方案3】:

    免责声明:肮脏的黑客,仅在 Android 4.4 上测试,但应该适用于 Android 4.0 及更高版本。

    需要尽早调用 setupURLStreamHandlerFactory() 方法,并使用URL.setURLStreamHandlerFactory() 设置我们自己的 URL 流处理程序工厂。工厂用于通过URL.openConnection() 打开的所有连接。我们为所有 http://... URL 设置了一个特殊的处理程序,它在创建的 HttpUrlConnection 上设置“Referer”标头。

    private static String REFERER_URL = "http://www.example.com/referer";
    
    private void setupURLStreamHandlerFactory() {
        URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
            @Override
            public URLStreamHandler createURLStreamHandler(String protocol) {
                if ("http".equals(protocol)) {
                    try {
                        String className = Build.VERSION.SDK_INT < 19 ?  "libcore.net.http.HttpHandler" : "com.android.okhttp.HttpHandler";
                        URLStreamHandler streamHandler = (URLStreamHandler) Class
                                .forName(className).newInstance();
                        return wrap(streamHandler);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
                return null;
            }
        });
    }
    
    private static URLStreamHandler wrap(final URLStreamHandler streamHandler) {
        return new URLStreamHandler() {
            @Override
            protected URLConnection openConnection(URL u) throws IOException {
                URLConnection conn;
                try {
                    Method openConnectionMethod = URLStreamHandler.class
                            .getDeclaredMethod("openConnection", URL.class);
                    openConnectionMethod.setAccessible(true);
                    conn = (URLConnection) openConnectionMethod.invoke(
                            streamHandler, u);
                    conn.setRequestProperty("Referer", REFERER_URL);
                    return conn;
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (IllegalArgumentException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    if (e.getTargetException() instanceof IOException) {
                        throw (IOException) e.getTargetException();
                    } else {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
    }
    

    编辑:修改后也适用于 Android 4.4 Kitkat 之前的版本

    【讨论】:

    • 这是一个非常聪明的技巧!但是它只能在 KitKat 中工作,不是吗?我不认为 OkHttp 以前存在过。
    • 我稍微修改了代码,所以它应该也适用于以前的版本。
    • 嗨,汉斯!我非常感激。您能否阅读我的更新,看看您的 hack 是否适用于我的情况?
    猜你喜欢
    • 2011-04-27
    • 2011-03-22
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多