कस्टम हेडर को WebView संसाधन अनुरोधों में जोड़ें - Android


95

मुझे WebView से आने वाले हर अनुरोध में कस्टम हेडर जोड़ने की आवश्यकता है। मुझे पता loadURLहै कि इसके लिए पैरामीटर है extraHeaders, लेकिन वे केवल प्रारंभिक अनुरोध पर लागू होते हैं। बाद के सभी अनुरोधों में शीर्षलेख शामिल नहीं हैं। मैंने सभी ओवरराइड्स को देखा है WebViewClient, लेकिन संसाधन अनुरोधों में हेडर जोड़ने के लिए कुछ भी अनुमति नहीं देता है - onLoadResource(WebView view, String url)। कोई भी मदद अद्भुत होगी।

धन्यवाद, रे


2
@MediumOne: यह एक बग नहीं है जितना कि यह एक विशेषता है जिसे आप लापता मानते हैं। मुझे HTTP विनिर्देश में कुछ भी पता नहीं है जो कहता है कि बाद के HTTP अनुरोधों को पिछले HTTP अनुरोधों से मनमाने हेडर को मिरर करना चाहिए।
कॉमन्सवेयर

1
@CommonsWare: "बाद में" शब्द यहाँ भ्रामक है। जब मैं facebook.com मुखपृष्ठ को लोड करने के लिए किसी भी ब्राउज़र पर " facebook.com " टाइप करता हूं , तो CSS, js और img फ़ाइलों को लोड करने के लिए कई "संसाधन अनुरोध" का समर्थन करते हैं। आप इसे F12 सुविधा (नेटवर्क टैब) का उपयोग करके क्रोम में देख सकते हैं। इन अनुरोधों के लिए, वेबव्यू हेडर नहीं जोड़ता है। मैंने Addons.mozilla.org/en-us/firefox/addon/modify-headers प्लग-इन का उपयोग करके फ़ायरफ़ॉक्स अनुरोधों में कस्टम हेडर जोड़ने का प्रयास किया। यह प्लगइन ऐसे सभी सपोर्टिंग "रिसोर्स रिक्वेस्ट" में हेडर जोड़ने में सक्षम था। मुझे लगता है कि WebView को भी ऐसा ही करना चाहिए।
माध्यमऑन

1
@MediumOne: "मुझे लगता है कि WebView को भी ऐसा ही करना चाहिए" - यह एक ऐसी विशेषता है जिसे आप गायब मानते हैं। ध्यान दें कि फ़ायरफ़ॉक्स ऐसा करने के लिए आपको एक प्लगइन का सहारा लेना पड़ा था। मैं यह नहीं कह रहा हूं कि आपकी प्रस्तावित विशेषता एक बुरा विचार है। मैं यह कह रहा हूं कि इसे बग के रूप में चिह्नित करना आपके प्रस्तावित फीचर को एंड्रॉइड में जोड़ने के लिए आपके कारण की मदद करने की संभावना नहीं है।
कॉमन्सवेयर

1
@CommonsWare: मान लीजिए कि मैं एक ब्राउज़र बनाने के लिए एक WebView का उपयोग कर रहा हूं जिसे कस्टम HTTP प्रॉक्सी के साथ काम करने के लिए कॉन्फ़िगर किया जा सकता है। यह प्रॉक्सी कस्टम प्रमाणीकरण का उपयोग करता है जहां इसके लिए अनुरोध किए जाने पर कस्टम हेडर होना चाहिए। अब, वेबव्यू कस्टम हेडर सेट करने के लिए एक एपीआई प्रदान करता है, लेकिन आंतरिक रूप से, यह सभी संसाधन अनुरोधों को हेडर सेट नहीं कर रहा है जो इसे उत्पन्न करता है। इन अनुरोधों के लिए हेडर सेट करने के लिए कोई अतिरिक्त API नहीं हैं। इसलिए, कोई भी सुविधा जो वेबदेहर अनुरोधों में कस्टम हेडर जोड़ने पर निर्भर करती है, विफल हो जाती है।
मध्याह्न

1
@CommonsWare - मैं 4 साल बाद इस बातचीत पर फिर से विचार कर रहा हूं। मैं अब सहमत हूं - यह एक बग नहीं होना चाहिए। HTTP विनिर्देश में ऐसा कुछ भी नहीं है जो कहता है कि बाद के अनुरोधों को उसी हेडर को भेजना चाहिए। :)
मीडियम

जवाबों:


80

प्रयत्न

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

अनुरोध लोड करने के संसाधनों में शीर्ष लेख जोड़ने के लिए, कस्टम WebViewClient और ओवरराइड करें:

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

9
क्षमा करें, लेकिन यह काम नहीं करता है। यह केवल हेडर लागू करता है प्रारंभिक अनुरोध भी। शीर्ष लेख संसाधन अनुरोधों में नहीं जोड़े गए हैं। अन्य विचार? धन्यवाद।
रे

19
हां, इस तरह से WebClient.shouldOverrideUrlLoading को ओवरराइड करें: सार्वजनिक बूलियन shouldOverrideUrlLoading (WebView देखें, स्ट्रिंग url) {view.loadUrl (url, extraHeaders); सच लौटना; }
peceps

5
@peceps - संसाधन लोडिंग के दौरान कॉलबैक 'shouldOverrideUrlLoading' को नहीं कहा जाता है। उदाहरण के लिए, जब हम कोशिश करते हैं view.loadUrl("http://www.facebook.com", extraHeaders), तो कई संसाधन अनुरोध होते हैं जैसे 'http://static.fb.com/images/logo.png'कि webiew से भेजे जाते हैं। इन अनुरोधों के लिए, अतिरिक्त हेडर नहीं जोड़े गए हैं। और ऐसे संसाधन अनुरोधों के दौरान shouldOverrideUrlLoading नहीं कहलाती। कॉलबैक 'ओनलॉडडोर्स' कहा जाता है, लेकिन इस बिंदु पर हेडर सेट करने का कोई तरीका नहीं है।
मीडियमऑन

2
@MediumOne, संसाधन लोडिंग के लिए, अधिक के लिए API कीWebViewClient.shouldInterceptRequest(android.webkit.WebView view, java.lang.String url) जाँच करें को ओवरराइड करें ।
योर्क

3
@yorkw: यह विधि सभी संसाधन अनुरोध urls को कैप्चर करती है। लेकिन इन अनुरोधों में हेडर जोड़ने का कोई तरीका नहीं है। मेरा लक्ष्य सभी अनुरोधों में कस्टम HTTP हेडर जोड़ना है। यदि यह shouldInterceptRequestविधि का उपयोग करके प्राप्त किया जा सकता है , तो क्या आप बता सकते हैं कि कैसे?
मीडियम फन

36

आपको WebViewClient.shouldInterceptRequest का उपयोग करके प्रत्येक अनुरोध को रोकना होगा

प्रत्येक अवरोधन के साथ, आपको url लेने की आवश्यकता होगी, यह अनुरोध स्वयं करें, और सामग्री स्ट्रीम वापस करें:

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);

यदि आपका न्यूनतम API लक्ष्य 21 का स्तर है , तो आप नए का उपयोग कर सकते हैं InceptceptRequest जो आपको केवल URL के बजाय अतिरिक्त अनुरोध जानकारी (जैसे हेडर) देता है।


2
बस अगर कोई इस स्थिति का सामना करता है तो मैं इस ट्रिक का उपयोग कर सकता हूं। (यह वैसे भी एक अच्छा है।) यहाँ आपके लिए एक नोट है। चूंकि HTTP कंटेंट-टाइप हेडर, जिसमें वैकल्पिक पैरामीटर हो सकते हैं जैसे चारसेट, MIME प्रकार के लिए पूरी तरह से संगत नहीं है, WebResourceResponse कंस्ट्रक्टर के पहले पैरामीटर की आवश्यकता है, ताकि हमें किसी भी तरह से सामग्री प्रकार से MIME प्रकार के हिस्से को निकालना पड़े। RegExp जैसे अधिकांश मामलों के लिए काम करने के लिए सोच सकते हैं।
जेम्स चेन

2
इस घटना को हटा दिया गया है .. public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)इसके बजाय यहाँ
Hirdesh Vishwdewa

3
@ हिरदेशविश्वदेव - अंतिम वाक्य को देखो।
मार्टिन कोंकनी

2
आप अपने वेबव्यू और मापदंडों के रूप में संशोधित अनुरोध के साथ सुपरक्लास की कंइंटरसेप्ट रीपेस्ट विधि के परिणामों को वापस करके अपना लोड करना छोड़ सकते हैं। यह उन परिदृश्यों में विशेष रूप से उपयोगी है जहां आप URL पर आधारित ट्रिगर कर रहे हैं, इसे पुनः लोड पर नहीं बदल रहे हैं और एक अनंत लूप में चलेंगे। हालांकि नए अनुरोध उदाहरण के लिए बहुत धन्यवाद। चीजों को संभालने के जावा तरीके मेरे लिए अत्यधिक उलझे हुए हैं।
एरिक रिपेन

4
HttpClient का संकलन 23 और इसके बाद के संस्करण के साथ नहीं किया जा सकता है,
तमसे कोज़्मार

30

हो सकता है कि मेरी प्रतिक्रिया काफी देर से हो, लेकिन यह 21 स्तर से नीचे और ऊपर एपीआई को कवर करता है।

हेडर जोड़ने के लिए हमें हर अनुरोध को रोकना चाहिए और आवश्यक हेडर के साथ नया बनाना चाहिए।

इसलिए हम ओवरराइड करने के लिए की जरूरत है shouldInterceptRequest विधि दोनों ही मामलों में कहा जाता है: 21 के स्तर तक एपीआई के लिए 1.; 2. एपीआई स्तर 21+ के लिए

    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;
            }

        }
   });

यदि प्रतिक्रिया प्रकार संसाधित किया जाना चाहिए तो आप बदल सकते हैं

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

सेवा

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

और विधि जोड़ें

        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
इस पुरानी पोस्ट का जवाब देने के लिए क्षमा करें, लेकिन इस कोड के साथ मेरा ऐप पृष्ठ लोड करने के बजाय फ़ाइल डाउनलोड करने की कोशिश करता है (और यह विफल रहता है)।
जियाकोमो एम

आपका बहुत बहुत धन्यवाद!
एलेक्स 13

21

जैसा कि पहले उल्लेख किया गया है, आप ऐसा कर सकते हैं:

 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);

मैंने इसका परीक्षण किया और एमवीसी नियंत्रक के साथ मैंने हेडर का निरीक्षण करने के लिए अधिकृत विशेषता को बढ़ाया और हेडर वहां है।


मुझे इसे फिर से संबोधित करना होगा जब यह लिखा गया था और इसे किट-कैट के साथ कार्य किया गया था। मैंने लॉली पॉप के साथ कोशिश नहीं की है।
लेरॉय

जेली बीन या
मार्शमैलो

6
यह वह नहीं करता है जो ओपी पूछ रहा है। वह वेबव्यू द्वारा किए गए सभी अनुरोधों में हेडर जोड़ना चाहता है। यह कस्टम हेडर को केवल पहले अनुरोध में जोड़ता है
निंजाकोडर

यह वह नहीं है जो ओपी पूछ रहा है
अक्षय

मुझे पता है कि यह जवाब नहीं देता है कि ओपी क्या देख रहा था, लेकिन यह वही था जो मैं चाहता था, यानी एक WebViewIntent URL में एक अतिरिक्त हेडर जोड़ना। धन्यवाद, परवाह किए बिना!
जोशुआ पिंटर

9

यह मेरे लिए काम करता है:

  1. सबसे पहले आपको विधि बनाने की जरूरत है, जो आपके हेडर को आप अनुरोध करने के लिए जोड़ना चाहते हैं:

    private Map<String, String> getCustomHeaders()
    {
        Map<String, String> headers = new HashMap<>();
        headers.put("YOURHEADER", "VALUE");
        return headers;
    }
  2. दूसरा आपको WebViewClient बनाने की आवश्यकता है:

    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. अपने WebView में WebViewClient जोड़ें:

    webView.setWebViewClient(getWebViewClient());

उम्मीद है की यह मदद करेगा।


1
अच्छा लग रहा है, लेकिन क्या यह हेडर जोड़ता है, या यह हेडर को प्रतिस्थापित करता है ?
इवो ​​रेनकेमा

@IvoRenkema का loadUrl(String url, Map<String, String> additionalHttpHeaders) मतलब है कि अतिरिक्त हेडर जोड़ें
AbhinayMe

4

आपको अपने सभी हेडर को लोड करने के लिए लोड करने में सक्षम होना चाहिए और Java के HttpURNonnection का उपयोग करके अपने स्वयं के लोडपेज को लिखना चाहिए। फिर प्रतिक्रिया को प्रदर्शित करने के लिए वेबव्यू के लोडडेटा का उपयोग करें।

उन हेडरों तक कोई पहुंच नहीं है जो Google प्रदान करता है। वे एक JNI कॉल में हैं, जो WebView स्रोत में गहरी है।


1
क्या आपके पास अपने जवाब में जो कुछ भी कहा गया है, उसका कोई संदर्भ है। यदि आप अपने उत्तरों के साथ कार्यान्वयन संदर्भ देते हैं तो यह दूसरों के लिए उपयोगी होगा।
ह्रदयेश विश्वदेव

1

यहाँ HttpUrlConnection का उपयोग करते हुए एक कार्यान्वयन है:

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
    }
}

ध्यान दें कि यह POST अनुरोधों के लिए काम नहीं करता है क्योंकि WebResourceRequest POST डेटा प्रदान नहीं करता है। एक अनुरोध डेटा है - WebViewClient पुस्तकालय जो POST डेटा को इंटरसेप्ट करने के लिए जावास्क्रिप्ट इंजेक्शन वर्कअराउंड का उपयोग करता है।


0

इसने मेरे लिए काम किया। नीचे इस तरह WebViewClient बनाएं और अपने वेबव्यू के लिए वेबक्लिअर सेट करें। मुझे webview.loadDataWithBaseURL का उपयोग करना पड़ा क्योंकि मेरे यूआरएल (मेरी सामग्री में) में बेसल नहीं था, लेकिन केवल रिश्तेदार यूआरएल थे। आपको url सही ढंग से तभी मिलेगा जब loadDataWithBaseURL का उपयोग करके एक बेसल सेट हो।

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;
            }
        }
    };

}

मेरे लिए यह काम करता है: .post (reqbody) जहां RequestBody reqbody = RequestBody.create (शून्य, "");
कारोले

-2

आप इसका उपयोग कर सकते हैं:

@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
यह सिर्फ url को पुनः लोड करता रहता है, है ना?
ओनेरियन

-3

मैं उसी समस्या को लेकर आया और हल किया।

जैसा कि पहले कहा गया है कि आपको अपना कस्टम WebViewClient बनाने की जरूरत है और shouldInterceptRequest विधि को ओवरराइड करना चाहिए।

WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)

"खाली" WebResourceResponse वापस करते समय उस विधि को webView.loadUrl जारी करना चाहिए।

कुछ इस तरह:

@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);
}

इस विधि से view.loadUrl कॉलिंग अनुप्रयोग क्रैश करने लगता है
willcwf

@willcwf क्या आपके पास इस दुर्घटनाग्रस्त होने का उदाहरण है?
फ्रांसेस्को

@Francesco मेरा ऐप भी क्रैश हो गया
जियाकोमो एम

बहुत से लोग यह कहते हुए निराश कर रहे हैं कि यह दुर्घटना मदद नहीं कर रही है। कृपया अधिक विशिष्ट बनें, कुछ त्रुटि जानकारी लिखें।
फ्रांसेस्को

-14

इसे इस्तेमाल करो:

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

11
यह प्रश्न का उत्तर नहीं देता है
younes0

यह प्राधिकरण हेडर के रूप में वैसा ही नहीं है
व्लाद
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.