WebView 리소스 요청에 사용자 지정 헤더 추가-Android


95

WebView에서 오는 모든 요청에 ​​사용자 지정 헤더를 추가해야합니다. 에 loadURL대한 매개 변수가 있다는 것을 알고 extraHeaders있지만 초기 요청에만 적용됩니다. 모든 후속 요청에는 헤더가 포함되지 않습니다. 에서 모든 재정의를 살펴 보았지만 WebViewClient리소스 요청에 헤더를 추가 할 수있는 것은 없습니다 onLoadResource(WebView view, String url). 어떤 도움이라도 좋을 것입니다.

고마워, 레이


2
@MediumOne : 이것은 당신이 놓치고 있다고 생각하는 기능만큼 버그가 아닙니다. HTTP 사양에서 후속 HTTP 요청이 이전 HTTP 요청의 임의의 헤더를 미러링해야한다는 내용을 알지 못합니다.
CommonsWare

1
@CommonsWare : 여기서 "후속"이라는 단어는 오해의 소지가 있습니다. facebook.com 홈페이지를로드하기 위해 브라우저에 " facebook.com "을 입력 하면 CSS, js 및 img 파일을로드하기위한 여러 지원 "리소스 요청"이 있습니다. Chrome에서 F12 기능 (네트워크 탭)을 사용하여이를 확인할 수 있습니다. 이러한 요청의 경우 웹보기는 헤더를 추가하지 않습니다. addons.mozilla.org/en-us/firefox/addon/modify-headers 플러그인을 사용하여 FireFox 요청에 사용자 정의 헤더를 추가해 보았습니다 . 이 플러그인은 지원하는 모든 "자원 요청"에 헤더를 추가 할 수있었습니다. WebView가 똑같이해야한다고 생각합니다.
MediumOne 2013

1
@MediumOne : "WebView가 똑같이해야한다고 생각합니다."-이것은 누락 된 것으로 간주되는 기능입니다. Firefox가이 작업을 수행하도록하려면 플러그인을 사용해야합니다. 나는 당신이 제안한 기능이 나쁜 생각이라고 말하는 것이 아닙니다. 나는 그것을 버그로 특성화하는 것이이 제안 된 기능을 Android에 추가하는 데 도움이되지 않을 것이라고 말하고 있습니다.
CommonsWare

1
@CommonsWare : 사용자 지정 HTTP 프록시와 함께 작동하도록 구성 할 수있는 브라우저를 구축하기 위해 WebView를 사용한다고 가정합니다. 이 프록시는 요청에 사용자 지정 헤더가 있어야하는 사용자 지정 인증을 사용합니다. 이제 webview는 사용자 지정 헤더를 설정하는 API를 제공하지만 내부적으로는 생성하는 모든 리소스 요청에 헤더를 설정하지 않습니다. 이러한 요청에 대한 헤더를 설정하는 추가 API도 없습니다. 따라서 WebView 요청에 사용자 지정 헤더를 추가하는 데 의존하는 모든 기능이 실패합니다.
MediumOne 2013

1
@CommonsWare-4 년 후에이 대화를 다시 검토하고 있습니다. 지금 동의합니다. 이것은 버그가 아니어야합니다. HTTP 사양에는 후속 요청이 동일한 헤더를 전송해야한다는 내용이 없습니다. :)
MediumOne 2017-06-04

답변:


81

시험

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

리소스로드 요청에 헤더를 추가하려면 사용자 지정 WebViewClient를 만들고 다음을 재정의합니다.

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

9
죄송합니다. 작동하지 않습니다. 초기 요청에도 헤더 만 적용합니다. 리소스 요청에 헤더가 추가되지 않습니다. 다른 아이디어? 감사.
Ray

19
예, WebClient.shouldOverrideUrlLoading을 다음과 같이 재정의합니다. public boolean shouldOverrideUrlLoading (WebView view, String url) {view.loadUrl (url, extraHeaders); true를 반환하십시오. }
peceps 2011

5
@peceps- 'shouldOverrideUrlLoading'콜백은 리소스로드 중에 호출되지 않습니다. 예를 들어를 시도 하면 webiew에서 전송되는 etc view.loadUrl("http://www.facebook.com", extraHeaders)와 같은 여러 리소스 요청 'http://static.fb.com/images/logo.png'이 있습니다. 이러한 요청의 경우 추가 헤더가 추가되지 않습니다. 그리고 shouldOverrideUrlLoading은 이러한 리소스 요청 중에 호출되지 않습니다. 콜백 'OnLoadResource'가 호출되지만이 시점에서 헤더를 설정할 방법이 없습니다.
MediumOne 2013 년

2
리소스 로딩의 경우 @MediumOne, 자세한 내용 은 WebViewClient.shouldInterceptRequest(android.webkit.WebView view, java.lang.String url)Check out API 를 재정의 하십시오 .
yorkw

3
@yorkw :이 메서드는 모든 리소스 요청 URL을 캡처합니다. 그러나 이러한 요청에 헤더를 추가하는 방법은 없습니다. 내 목표는 모든 요청에 ​​사용자 지정 HTTP 헤더를 추가하는 것입니다. 이 shouldInterceptRequest방법을 사용하여 달성 할 수 있다면 방법을 설명해 주시겠습니까?
MediumOne 2013

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 인 경우 URL 대신 추가 요청 정보 (예 : 헤더)를 제공 하는 새로운 shouldInterceptRequest 를 사용할 수 있습니다 .


2
누군가가이 트릭을 사용하는 동안 내가 가진 것과 같은 상황에 직면 할 경우를 대비하여. (어쨌든 이것은 좋은 것입니다.) 여기에 당신을위한 메모가 있습니다. charset과 같은 선택적 매개 변수를 포함 할 수있는 http content-type 헤더는 WebResourceResponse 생성자의 첫 번째 매개 변수 인 MIME 유형과 완전히 호환되지 않으므로 어떤 의미로든 content-type에서 MIME 유형 부분을 추출해야합니다. RegExp와 같이 대부분의 경우에 작동하도록 생각할 수 있습니다.
제임스 첸

2
이 이벤트는 사용되지 않습니다 .. 사용 public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)더 찾을 대신 여기
Hirdesh Vishwdewa

3
@HirdeshVishwdewa-마지막 문장을보세요.
Martin Konecny ​​2015 년

2
웹뷰 및 수정 된 요청을 매개 변수로 사용하여 수퍼 클래스의 shouldInterceptRequest 메소드 결과를 반환하여 자체로드 수행을 건너 뛸 수 있습니다. 이는 URL을 기반으로 트리거하고 다시로드 할 때 변경하지 않고 무한 루프가 발생하는 시나리오에서 특히 유용합니다. 그래도 새로운 요청 예제에 감사드립니다. 일을 처리하는 Java 방법은 나에게 매우 반 직관적입니다.
Erik Reppen

4
HttpClient를가 compileSdk (23)와 이상 사용할 수 없습니다
타마스 Kozmér에게

30

어쩌면 내 대답은 아주 늦게, 그러나 그것은 API를 커버 아래위의 21 수준.

헤더를 추가하려면 모든 요청을 가로 채서 필요한 헤더로 요청만들어야 합니다.

따라서 두 경우 모두에서 호출되는 shouldInterceptRequest 메서드 를 재정의해야합니다 . 1. 레벨 21까지의 API; 2. API 레벨 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
이 이전 게시물에 대한 답변을 드리게되어 죄송합니다.하지만이 코드를 사용하면 내 앱이 페이지를로드하는 대신 파일을 다운로드하려고하는데 실패합니다.
Giacomo M

대단히 감사합니다!
AlexS 2011

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

나는 헤더를 검사하기 위해 Authorize Attribute를 확장 한 MVC 컨트롤러로 이것을 테스트했고 헤더가 거기에 있습니다.


이 글을 작성하고 게시했을 때 Kit-Kat과 함께 작동하는 것처럼 다시 언급해야합니다. Lolly Pop은 시도하지 않았습니다.
leeroya

아니 헤더에 아무 것도 변경하지 않습니다 ... 젤리 콩이나 산들 바람에 날 위해 일
에릭 Verboom

6
이것은 OP가 요구하는 것을 수행하지 않습니다. 그는 webview에서 만든 모든 요청에 ​​헤더를 추가하려고합니다. 이것은 첫 번째 요청에만 사용자 정의 헤더를 추가합니다
NinjaCoder

이것은 OP가 요구하는 것이 아닙니다
Akshay

나는 이것이 OP가 찾던 것에 대한 대답이 아니라는 것을 알고 있지만 이것은 정확히 내가 원하는 것입니다. 즉, WebViewIntent URL에 추가 헤더를 추가하는 것입니다. 고마워요!
Joshua Pinter

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. WebViewClient를 WebView에 추가합니다.

    webView.setWebViewClient(getWebViewClient());

도움이 되었기를 바랍니다.


1
외모 좋은, 그러나 이것은 않는 추가 헤더를, 또는이 않는 대체 헤더를?
Ivo Renkema

@IvoRenkema loadUrl(String url, Map<String, String> additionalHttpHeaders) 는 추가 헤더 추가를 의미합니다
AbhinayMe

4

loadUrl을 건너 뛰고 Java의 HttpURLConnection을 사용하여 자체 loadPage를 작성하여 모든 헤더를 제어 할 수 있어야합니다. 그런 다음 webview의 loadData를 사용하여 응답을 표시합니다.

Google이 제공하는 헤더에 대한 액세스 권한이 없습니다. WebView 소스의 깊은 곳에서 JNI 호출에 있습니다.


1
당신의 대답에 당신이 말한 것에 대한 언급이 있습니까 .. 당신의 대답과 함께 구현 참조를 제공하면 다른 사람들에게 도움이 될 것입니다.
Hirdesh Vishwdewa

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

WebResourceRequest는 POST 데이터를 제공하지 않기 때문에 POST 요청에는 작동하지 않습니다. POST 데이터를 가로 채기 위해 JavaScript 주입 해결 방법을 사용 하는 Request Data-WebViewClient 라이브러리 가 있습니다.


0

이것은 나를 위해 일했습니다. 아래와 같이 WebViewClient를 만들고 webclient를 webview로 설정하십시오. 내 URL (내 콘텐츠)에 baseurl이 없지만 상대 URL 만 있으므로 webview.loadDataWithBaseURL을 사용해야했습니다. loadDataWithBaseURL을 사용하여 설정된 baseurl이있는 경우에만 URL을 올바르게 가져옵니다.

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 (null, "");
Karoly

-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을 계속 다시로드합니다.
Onheiron

-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

내 응용 프로그램 @Francesco 또한 충돌
자코모 M

너무 많은 사람들이 충돌이 도움이되지 않는다고 말하면서 이것을 반대하고 있습니다. 더 구체적으로 설명하고 오류 정보를 작성하십시오.
Francesco

-14

이것을 사용하십시오 :

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

11
이 질문에 대답하지 않습니다
younes0

이것은 인증 헤더와 동일하지 않습니다
Vlad
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.