Web Görünümü kaynak isteklerine özel başlıklar ekleme - Android


98

Web Görünümü’nden gelen HER isteğe özel başlıklar eklemem gerekiyor. İçin loadURLparametrenin olduğunu biliyorum extraHeaders, ancak bunlar yalnızca ilk isteğe uygulanıyor. Sonraki tüm istekler başlıkları içermez. Tüm geçersiz kılmaları inceledim WebViewClient, ancak hiçbir şey kaynak isteklerine başlık eklemeye izin vermiyor - onLoadResource(WebView view, String url). Herhangi bir yardım harika olurdu.

Teşekkürler Ray


2
@MediumOne: Eksik olduğunu düşündüğünüz bir özellik olduğu kadar bu bir hata değil. HTTP belirtiminde, sonraki HTTP isteklerinin önceki HTTP isteklerinden rastgele başlıkları yansıtması gerektiğini söyleyen herhangi bir şey bilmiyorum.
CommonsWare

1
@CommonsWare: "Sonraki" kelimesi burada yanıltıcıdır. Facebook.com ana sayfasını yüklemek için herhangi bir tarayıcıya " facebook.com " yazdığımda , CSS, js ve img dosyalarını yüklemek için çeşitli destekleyici "kaynak istekleri" var. Bunu, Chrome'da F12 özelliğini (Ağ sekmesi) kullanarak kontrol edebilirsiniz. Bu istekler için web görünümü üstbilgi eklemez. Addons.mozilla.org/en-us/firefox/addon/modify-headers eklentisini kullanarak FireFox isteklerine özel başlıklar eklemeyi denedim . Bu eklenti, bu tür destekleyici tüm "kaynak taleplerine" başlıklar ekleyebildi. Bence WebView da aynısını yapmalı.
MediumOne

1
@MediumOne: "Bence WebView aynısını yapmalı" - ki bu eksik olduğunu düşündüğünüz bir özellik. Firefox'un bunu yapması için bir eklentiye başvurmanız gerektiğini unutmayın. Önerilen özelliğinizin kötü bir fikir olduğunu söylemiyorum. Bunu bir hata olarak nitelemenin, amacınızın bu önerilen özelliğin Android'e eklenmesine yardımcı olma ihtimalinin düşük olduğunu söylüyorum.
CommonsWare

1
@CommonsWare: Özel bir HTTP proxy ile çalışacak şekilde yapılandırılabilen bir tarayıcı oluşturmak için bir WebView kullandığımı varsayalım. Bu proxy, kendisine gelen isteklerin özel bir başlığa sahip olması gereken özel kimlik doğrulamasını kullanır. Artık webview, özel üstbilgiler ayarlamak için bir API sağlar, ancak dahili olarak, üstbilgiyi oluşturduğu tüm kaynak isteklerine ayarlamaz. Bu istekler için üstbilgi ayarlamak için ek API'ler de yoktur. Bu nedenle, WebView isteklerine özel başlıklar eklemeye dayanan herhangi bir özellik başarısız olur.
MediumOne

1
@CommonsWare - 4 yıl sonra bu konuşmayı tekrar ziyaret ediyorum. Şimdi katılıyorum - bu bir hata olmamalı. HTTP spesifikasyonunda, sonraki isteklerin aynı başlıkları göndermesi gerektiğini söyleyen hiçbir şey yoktur. :)
MediumOne

Yanıtlar:


85

Deneyin

loadUrl(String url, Map<String, String> extraHeaders)

Kaynak yükleme isteklerine üstbilgi eklemek için özel WebViewClient yapın ve geçersiz kılın:

API 24+:
WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)
or
WebResourceResponse shouldInterceptRequest(WebView view, String url)

9
Üzgünüm ama bu işe yaramıyor. Yalnızca başlıkları ilk isteklerde de uygular. Başlıklar, kaynak isteklerine EKLENMEZ. Diğer fikirler? Teşekkürler.
Ray

19
Evet, WebClient.shouldOverrideUrlLoading'i şu şekilde geçersiz kıl: public boolean shouldOverrideUrlLoading (WebView görünümü, String url) {view.loadUrl (url, extraHeaders); doğruya dön; }
peceps

5
@peceps - kaynak yükleme sırasında 'shouldOverrideUrlLoading' geri çağrısı çağrılmaz. Örneğin, denediğimizde view.loadUrl("http://www.facebook.com", extraHeaders), web görüntülemesinden 'http://static.fb.com/images/logo.png'gönderilen vb. Gibi birden çok kaynak isteği vardır. Bu istekler için fazladan başlıklar eklenmez. Ve shouldOverrideUrlLoading bu tür kaynak talepleri sırasında çağrılmaz. Geri çağırma 'OnLoadResource' çağrılır, ancak bu noktada başlıkları ayarlamanın yolu yoktur.
MediumOne

2
@MediumOne, kaynak yükleme için, daha fazlası için API'ye göz WebViewClient.shouldInterceptRequest(android.webkit.WebView view, java.lang.String url)atın .
yorkw

3
@yorkw: Bu yöntem tüm kaynak istek url'lerini yakalar. Ancak bu isteklere başlık eklemenin bir yolu yoktur. Amacım, tüm isteklere özel HTTP üstbilgileri eklemek. Bu shouldInterceptRequestyöntem kullanılarak başarılabiliyorsa , nasıl yapılacağını açıklar mısınız?
MediumOne

36

WebViewClient.shouldInterceptRequest'i kullanarak her isteği yakalamanız gerekecek

Her müdahalede, url'yi almanız, bu isteği kendiniz yapmanız ve içerik akışını döndürmeniz gerekecek:

WebViewClient wvc = new WebViewClient() {
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {

        try {
            DefaultHttpClient client = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("MY-CUSTOM-HEADER", "header value");
            httpGet.setHeader(HttpHeaders.USER_AGENT, "custom user-agent");
            HttpResponse httpReponse = client.execute(httpGet);

            Header contentType = httpReponse.getEntity().getContentType();
            Header encoding = httpReponse.getEntity().getContentEncoding();
            InputStream responseInputStream = httpReponse.getEntity().getContent();

            String contentTypeValue = null;
            String encodingValue = null;
            if (contentType != null) {
                contentTypeValue = contentType.getValue();
            }
            if (encoding != null) {
                encodingValue = encoding.getValue();
            }
            return new WebResourceResponse(contentTypeValue, encodingValue, responseInputStream);
        } catch (ClientProtocolException e) {
            //return null to tell WebView we failed to fetch it WebView should try again.
            return null;
        } catch (IOException e) {
             //return null to tell WebView we failed to fetch it WebView should try again.
            return null;
        }
    }
}

Webview wv = new WebView(this);
wv.setWebViewClient(wvc);

Minimum API hedefiniz düzey 21 ise , yalnızca URL yerine size ek istek bilgileri (başlıklar gibi) veren yeni shouldInterceptRequest'i kullanabilirsiniz .


2
Birisinin bu numarayı kullanırken sahip olduğum aynı durumla karşılaşması durumunda. (Bu yine de iyi.) İşte size bir not. Karakter kümesi gibi isteğe bağlı parametreler içerebilen http içerik türü başlığı, MIME türü ile tam olarak uyumlu olmadığından, WebResourceResponse yapıcısının ilk parametresinin gerekliliği, böylece MIME türü bölümünü içerik türünden herhangi bir şekilde çıkarmamız gerekir. çoğu durumda çalışmasını sağlamak için RegExp gibi düşünebilir.
James Chen

2
Bu etkinlik kullanımdan kaldırıldı .. public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)onun yerine burada
Hirdesh Vishwdewa

3
@HirdeshVishwdewa - son cümleye bakın.
Martin Konecny

2
Üst sınıfın shouldInterceptRequest yönteminin sonuçlarını web görünümünüz ve değiştirilmiş isteğinizle parametreler olarak döndürerek kendi yükünüzü yapmayı atlayabilirsiniz. Bu, özellikle URL'ye göre tetiklediğiniz, yeniden yüklemede değiştirmediğiniz ve sonsuz bir döngüye gireceği senaryolarda kullanışlıdır. Yine de yeni istek örneği için çok teşekkürler. Java'nın işleri halletme yöntemleri benim için son derece mantıksız.
Erik Reppen

4
HttpClient, compileSdk 23 ve üstü ile kullanılamaz,
Tamás Kozmér

30

Belki cevabım oldukça geç, ancak 21 seviyesinin altındaki ve üzerindeki API'yi kapsıyor .

Başlıklar eklemek için her isteği durdurmalı ve gerekli başlıklarla yeni bir tane oluşturmalıyız .

Bu yüzden her iki durumda da çağrılan shouldInterceptRequest yöntemini geçersiz kılmamız gerekiyor : 1. API için seviye 21'e kadar; 2. API seviyesi 21+ için

    webView.setWebViewClient(new WebViewClient() {

        // Handle API until level 21
        @SuppressWarnings("deprecation")
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {

            return getNewResponse(url);
        }

        // Handle API 21+
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

            String url = request.getUrl().toString();

            return getNewResponse(url);
        }

        private WebResourceResponse getNewResponse(String url) {

            try {
                OkHttpClient httpClient = new OkHttpClient();

                Request request = new Request.Builder()
                        .url(url.trim())
                        .addHeader("Authorization", "YOU_AUTH_KEY") // Example header
                        .addHeader("api-key", "YOUR_API_KEY") // Example header
                        .build();

                Response response = httpClient.newCall(request).execute();

                return new WebResourceResponse(
                        null,
                        response.header("content-encoding", "utf-8"),
                        response.body().byteStream()
                );

            } catch (Exception e) {
                return null;
            }

        }
   });

Yanıt türünün işlenmesi gerekiyorsa, değiştirebilirsiniz

        return new WebResourceResponse(
                null, // <- Change here
                response.header("content-encoding", "utf-8"),
                response.body().byteStream()
        );

-e

        return new WebResourceResponse(
                getMimeType(url), // <- Change here
                response.header("content-encoding", "utf-8"),
                response.body().byteStream()
        );

ve yöntem ekle

        private String getMimeType(String url) {
            String type = null;
            String extension = MimeTypeMap.getFileExtensionFromUrl(url);

            if (extension != null) {

                switch (extension) {
                    case "js":
                        return "text/javascript";
                    case "woff":
                        return "application/font-woff";
                    case "woff2":
                        return "application/font-woff2";
                    case "ttf":
                        return "application/x-font-ttf";
                    case "eot":
                        return "application/vnd.ms-fontobject";
                    case "svg":
                        return "image/svg+xml";
                }

                type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
            }

            return type;
        }

1
Bu eski gönderiyi yanıtladığım için üzgünüm, ancak bu kodla uygulamam sayfayı yüklemek yerine dosyayı indirmeye çalışıyor (ve başarısız oluyor).
Giacomo M

bu gönderi talepleri için kullanılabilir mi?
mrid

21

Daha önce de belirtildiği gibi, bunu yapabilirsiniz:

 WebView  host = (WebView)this.findViewById(R.id.webView);
 String url = "<yoururladdress>";

 Map <String, String> extraHeaders = new HashMap<String, String>();
 extraHeaders.put("Authorization","Bearer"); 
 host.loadUrl(url,extraHeaders);

Bunu ve başlığı incelemek için Yetkilendirme Özniteliğini genişlettiğim bir MVC Denetleyicisi ile test ettim ve başlık orada.


Kit-Kat ile birlikte çalıştığı ve yazıldığı zamanki gibi bu konuyu tekrar ele almam gerekecek. Lolly Pop ile denemedim.
leeroya

Değil başlıklardaki şey değişmez ... Jöle fasulye veya Marshmallow benim için çalışan
Erik Verboom

6
Bu, OP'nin istediği şeyi yapmaz. Web görünümü tarafından yapılan tüm isteklere üstbilgi eklemek istiyor. Bu, yalnızca ilk isteğe özel başlık ekler
NinjaCoder

OP'nin sorduğu bu değil
Akshay

Bunun OP'nin aradığı yanıtı vermediğini biliyorum ama tam olarak istediğim buydu, yani bir WebViewIntent URL'sine fazladan bir başlık eklemek. Ne olursa olsun teşekkürler!
Joshua Pinter

10

Bu benim için çalışıyor:

  1. Öncelikle, isteğe eklemek istediğiniz başlıklarınızı döndürecek bir yöntem oluşturmanız gerekir:

    private Map<String, String> getCustomHeaders()
    {
        Map<String, String> headers = new HashMap<>();
        headers.put("YOURHEADER", "VALUE");
        return headers;
    }
    
  2. İkinci olarak WebViewClient oluşturmanız gerekir:

    private WebViewClient getWebViewClient()
    {
    
        return new WebViewClient()
        {
    
        @Override
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
        {
            view.loadUrl(request.getUrl().toString(), getCustomHeaders());
            return true;
        }
    
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url)
        {
            view.loadUrl(url, getCustomHeaders());
            return true;
        }
    };
    }
    
  3. Web Görünümünüze WebViewClient ekleyin:

    webView.setWebViewClient(getWebViewClient());
    

Bu yardımcı olur umarım.


1
İyi görünüyor, ancak bu bir başlık ekliyor mu yoksa bu başlıkların yerini mi alıyor?
Ivo Renkema

@IvoRenkema loadUrl(String url, Map<String, String> additionalHttpHeaders) , başlık ekleme anlamına gelir
AbhinayMe

4

LoadUrl'yi atlayarak ve Java'nın HttpURLConnection kullanarak kendi loadPage'inizi yazarak tüm başlıklarınızı kontrol edebilmelisiniz. Ardından yanıtı görüntülemek için web görünümünün loadData'sını kullanın.

Google'ın sağladığı başlıklara erişim yoktur. WebView kaynağının derinliklerinde bir JNI çağrısındalar.


1
Cevabınızda söylediklerinize referans var mı? Cevaplarınızla uygulama referansları vermeniz başkaları için faydalı olacaktır.
Hirdesh Vishwdewa

1

İşte HttpUrlConnection kullanan bir uygulama:

class CustomWebviewClient : WebViewClient() {
    private val charsetPattern = Pattern.compile(".*?charset=(.*?)(;.*)?$")

    override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
        try {
            val connection: HttpURLConnection = URL(request.url.toString()).openConnection() as HttpURLConnection
            connection.requestMethod = request.method
            for ((key, value) in request.requestHeaders) {
                connection.addRequestProperty(key, value)
            }

            connection.addRequestProperty("custom header key", "custom header value")

            var contentType: String? = connection.contentType
            var charset: String? = null
            if (contentType != null) {
                // some content types may include charset => strip; e. g. "application/json; charset=utf-8"
                val contentTypeTokenizer = StringTokenizer(contentType, ";")
                val tokenizedContentType = contentTypeTokenizer.nextToken()

                var capturedCharset: String? = connection.contentEncoding
                if (capturedCharset == null) {
                    val charsetMatcher = charsetPattern.matcher(contentType)
                    if (charsetMatcher.find() && charsetMatcher.groupCount() > 0) {
                        capturedCharset = charsetMatcher.group(1)
                    }
                }
                if (capturedCharset != null && !capturedCharset.isEmpty()) {
                    charset = capturedCharset
                }

                contentType = tokenizedContentType
            }

            val status = connection.responseCode
            var inputStream = if (status == HttpURLConnection.HTTP_OK) {
                connection.inputStream
            } else {
                // error stream can sometimes be null even if status is different from HTTP_OK
                // (e. g. in case of 404)
                connection.errorStream ?: connection.inputStream
            }
            val headers = connection.headerFields
            val contentEncodings = headers.get("Content-Encoding")
            if (contentEncodings != null) {
                for (header in contentEncodings) {
                    if (header.equals("gzip", true)) {
                        inputStream = GZIPInputStream(inputStream)
                        break
                    }
                }
            }
            return WebResourceResponse(contentType, charset, status, connection.responseMessage, convertConnectionResponseToSingleValueMap(connection.headerFields), inputStream)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return super.shouldInterceptRequest(view, request)
    }

    private fun convertConnectionResponseToSingleValueMap(headerFields: Map<String, List<String>>): Map<String, String> {
        val headers = HashMap<String, String>()
        for ((key, value) in headerFields) {
            when {
                value.size == 1 -> headers[key] = value[0]
                value.isEmpty() -> headers[key] = ""
                else -> {
                    val builder = StringBuilder(value[0])
                    val separator = "; "
                    for (i in 1 until value.size) {
                        builder.append(separator)
                        builder.append(value[i])
                    }
                    headers[key] = builder.toString()
                }
            }
        }
        return headers
    }
}

WebResourceRequest POST verisi sağlamadığından, bunun POST istekleri için çalışmadığını unutmayın. POST verilerini yakalamak için bir JavaScript enjeksiyon geçici çözümü kullanan bir İstek Verisi - WebViewClient kitaplığı vardır.


0

Bu benim için çalıştı. Aşağıdaki gibi WebViewClient oluşturun ve web istemcisini web görünümünüze ayarlayın. Webview.loadDataWithBaseURL'yi kullanmak zorunda kaldım çünkü url'lerim (içeriğimde) baseurl'e sahip değil, sadece göreli url'lere sahipti. Url'yi yalnızca loadDataWithBaseURL kullanarak bir baseurl kümesi olduğunda doğru şekilde alırsınız.

public WebViewClient getWebViewClientWithCustomHeader(){
    return new WebViewClient() {
        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
            try {
                OkHttpClient httpClient = new OkHttpClient();
                com.squareup.okhttp.Request request = new com.squareup.okhttp.Request.Builder()
                        .url(url.trim())
                        .addHeader("<your-custom-header-name>", "<your-custom-header-value>")
                        .build();
                com.squareup.okhttp.Response response = httpClient.newCall(request).execute();

                return new WebResourceResponse(
                        response.header("content-type", response.body().contentType().type()), // You can set something other as default content-type
                        response.header("content-encoding", "utf-8"),  // Again, you can set another encoding as default
                        response.body().byteStream()
                );
            } catch (ClientProtocolException e) {
                //return null to tell WebView we failed to fetch it WebView should try again.
                return null;
            } catch (IOException e) {
                //return null to tell WebView we failed to fetch it WebView should try again.
                return null;
            }
        }
    };

}

benim için çalışıyor: .post (reqbody) burada RequestBody reqbody = RequestBody.create (null, "");
Karoly

-2

Bunu kullanabilirsiniz:

@Override

 public boolean shouldOverrideUrlLoading(WebView view, String url) {

                // Here put your code
                Map<String, String> map = new HashMap<String, String>();
                map.put("Content-Type","application/json");
                view.loadUrl(url, map);
                return false;

            }

2
Bu sadece url'yi yeniden yüklemeye devam ediyor, değil mi?
Onheiron

-3

Aynı problemle karşılaştım ve çözdüm.

Daha önce de belirtildiği gibi, özel WebViewClient'inizi oluşturmanız ve shouldInterceptRequest yöntemini geçersiz kılmanız gerekir.

WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)

Bu yöntem, "boş" bir WebResourceResponse döndürürken bir webView.loadUrl yayınlamalıdır.

Bunun gibi bir şey:

@Override
public boolean shouldInterceptRequest(WebView view, WebResourceRequest request) {

    // Check for "recursive request" (are yor header set?)
    if (request.getRequestHeaders().containsKey("Your Header"))
        return null;

    // Add here your headers (could be good to import original request header here!!!)
    Map<String, String> customHeaders = new HashMap<String, String>();
    customHeaders.put("Your Header","Your Header Value");
    view.loadUrl(url, customHeaders);

    return new WebResourceResponse("", "", null);
}

Bu yöntemden view.loadUrl çağrısı yapmak uygulamayı
kilitliyor

@willcwf bunun çökmesine bir örnek var mı?
Francesco

@Francesco uygulamam da çöküyor
Giacomo M

Çok fazla şey buna olumsuz oy veriyor, çökmenin yardımcı olmadığını söylüyor. Lütfen daha spesifik olun, bazı hata bilgileri yazın.
Francesco

-14

Bunu kullan:

webView.getSettings().setUserAgentString("User-Agent");

11
bu soruya cevap vermiyor
younes0

bu, yetkilendirme başlığı ile aynı değil
Vlad
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.