I need to add custom headers to EVERY request coming from the WebView. I know loadURL has the parameter for extraHeaders, but those are only applied to the initial request. All subsequent requests do not contain the headers. I have looked at all overrides in WebViewClient, but nothing allows for adding headers to resource requests - onLoadResource(WebView view, String url). Any help would be wonderful.
Thanks,
Ray
Try
loadUrl(String url, Map<String, String> extraHeaders)
For adding headers to resources loading requests, make custom WebViewClient and override:
API 24+:
WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)
or
WebResourceResponse shouldInterceptRequest(WebView view, String url)
You will need to intercept each request using WebViewClient.shouldInterceptRequest
With each interception, you will need to take the url, make this request yourself, and return the content stream:
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);
If your minimum API target is level 21, you can use the new shouldInterceptRequest which gives you additional request information (such as headers) instead of just the URL.
Maybe my response quite late, but it covers API below and above 21 level.
To add headers we should intercept every request and create new one with required headers.
So we need to override shouldInterceptRequest method called in both cases:
1. for API until level 21;
2. for API level 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;
}
}
});
If response type should be processed you could change
return new WebResourceResponse(
null, // <- Change here
response.header("content-encoding", "utf-8"),
response.body().byteStream()
);
to
return new WebResourceResponse(
getMimeType(url), // <- Change here
response.header("content-encoding", "utf-8"),
response.body().byteStream()
);
and add method
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;
}
As mentioned before, you can do this:
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);
I tested this and on with a MVC Controller that I extended the Authorize Attribute to inspect the header and the header is there.
This works for me:
First you need to create method, which will be returns your
headers you want to add to request:
private Map<String, String> getCustomHeaders()
{
Map<String, String> headers = new HashMap<>();
headers.put("YOURHEADER", "VALUE");
return headers;
}
Second you need to create 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;
}
};
}
Add WebViewClient to your WebView:
webView.setWebViewClient(getWebViewClient());
Hope this helps.
You should be able to control all your headers by skipping loadUrl and writing your own loadPage using Java's HttpURLConnection. Then use the webview's loadData to display the response.
There is no access to the headers which Google provides. They are in a JNI call, deep in the WebView source.
Here is an implementation using 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
}
}
Note that this does not work for POST requests because WebResourceRequest doesn't provide POST data. There is a Request Data - WebViewClient library which uses a JavaScript injection workaround for intercepting POST data.
This worked for me. Create WebViewClient like this below and set the webclient to your webview. I had to use webview.loadDataWithBaseURL as my urls (in my content) did not have the baseurl but only relative urls. You will get the url correctly only when there is a baseurl set using 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;
}
}
};
}
You can use this:
#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;
}
I came accross the same problem and solved.
As said before you need to create your custom WebViewClient and override the shouldInterceptRequest method.
WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)
That method should issue a webView.loadUrl while returning an "empty" WebResourceResponse.
Something like this:
#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);
}
Use this:
webView.getSettings().setUserAgentString("User-Agent");
Related
I am trying to return the content of a Json file. But I want to modify before sending it to the front end. I want to add "[" and "]" at the beginning and end of the file. I am doing that because the json file has multiple json root elements.
Like for example extract the result as illustrated in
result = restTemplate.executeRequest(HttpMethod.GET, String.class);
//change Body and put it back in result
Question
Is it possible to change the body of the response and put it back in ResponseEntity?
Source Code
public ResponseEntity<String> getScalityObject(String chainCode, String dataCenter, String path, String byteRange) {
Map<String, Object> queryParams = new HashMap<>();
if (dataCenter != null && !dataCenter.isEmpty()) {
queryParams.put("dataCenter", dataCenter);
}
if (byteRange != null && !byteRange.isEmpty()) {
queryParams.put("byteRange", byteRange);
}
String decodedStr = URLDecoder.decode(path);
queryParams.put("path", decodedStr);
reservationService.setContext(
RESA_INTERNAL_SERVICE_NAME,
queryParams,
"/chains/{chainCode}/objects/file",
chainCode);
restTemplate.setServiceDefinition(reservationService);
ResponseEntity<String> result;
try {
result = restTemplate.executeRequest(HttpMethod.GET, String.class);
//Change responseBody here
return result;
} catch (IOException e) {
e.printStackTrace();
result = new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return result;
}
public <T> ResponseEntity<T> executeRequest(HttpMethod method, Class<T> responseType) throws IOException {
if (this.serviceDefinition == null) {
throw new IllegalArgumentException("You haven't provided any service definition for this call. " +
"Are you sure you called the right method before using this Amadeus Rest Template?");
}
// Resolve the URI
URI url = this.serviceDefinition.getUriComponents().toUri();
// Add the extra headers if necessary
HttpHeaders headers = new HttpHeaders();
if (this.serviceDefinition.getHeaders() != null) {
for(Map.Entry<String,String> headerSet : this.serviceDefinition.getHeaders().entrySet()) {
headers.put(headerSet.getKey(), Arrays.asList(headerSet.getValue()));
}
}
HttpEntity entity = new HttpEntity(headers);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
RequestCallback requestCallback = httpEntityCallback(entity, responseType);
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
throw ex;
}
finally {
if (response != null) {
response.close();
}
}
}
One of the way which I can think of is :
ResponseEntity<String> result = restTemplate.executeRequest(HttpMethod.GET, String.class);
StringBuilder builder = new StringBuilder(result.getBody());
... //do your transformation to stringbuilder reference.
result = ResponseEntity.status(result.getStatusCode()).body(builder.toString());
Another way if you want to avoid this is to return String response from your executeRequest & modify that response before creating ResponseEntity.
Try this:
Create your own HttpMessageConverter, implementing:
public interface HttpMessageConverter<T> {
// Indicates whether the given class can be read by this converter.
boolean canRead(Class<?> clazz, MediaType mediaType);
// Indicates whether the given class can be written by this converter.
boolean canWrite(Class<?> clazz, MediaType mediaType);
// Return the list of {#link MediaType} objects supported by this converter.
List<MediaType> getSupportedMediaTypes();
// Read an object of the given type form the given input message, and returns it.
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
// Write an given object to the given output message.
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
Register the custom converter into your restTemplate object:
String url = "url";
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the String message converter
restTemplate.getMessageConverters().add(new YourConverter());
// Make the HTTP GET request, marshaling the response to a String
String result = restTemplate.getForObject(url, String.class);
I have implemented a zuul gateway service for the communication between some micro services that i have wrote. I have a specific scenario like i want to change the service path in one of my custom filter and redirected to some other service. Is this possible with the zuul gateway?. I have tried putting "requestURI" parameter with the updated uri to the request context in my route filter but that didn't worked out well
Please help me out guys
thanks in advance
yes, you can. for that you need to implement ZuulFilter with type PRE_TYPE, and update response with specified Location header and response status either 301 or 302.
#Slf4j
public class CustomRedirectFilter extends ZuulFilter {
#Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
#Override
public int filterOrder() {
return FilterConstants.SEND_FORWARD_FILTER_ORDER;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
String requestUrl = ctx.getRequest().getRequestURL().toString();
if (shouldBeRedirected(requestUrl)) {
String redirectUrl = generateRedirectUrl(ctx.getRequest());
sendRedirect(ctx.getResponse(), redirectUrl);
}
return null;
}
private void sendRedirect(HttpServletResponse response, String redirectUrl){
try {
response.setHeader(HttpHeaders.LOCATION, redirectUrl);
response.setStatus(HttpStatus.MOVED_PERMANENTLY.value());
response.flushBuffer();
} catch (IOException ex) {
log.error("Could not redirect to: " + redirectUrl, ex);
}
}
private boolean shouldBeRedirected(String requestUrl) {
// your logic whether should we redirect request or not
return true;
}
private String generateRedirectUrl(HttpServletRequest request) {
String queryParams = request.getQueryString();
String currentUrl = request.getRequestURL().toString() + (queryParams == null ? "" : ("?" + queryParams));
// update url
return updatedUrl;
}
}
I am trying to make get call like this:
#GET(AppConstants.BASE_URL + "{category_type}/")
Call<JsonObject> callCustomFilterApI(#Path("category_type") String type,
#QueryMap(encoded = true) Map<String,String> fields ,
#Query("page") String pageNo);
But #QueryMap can have "&" in the data, so retrofit encoding it to %26.
Is there anyway "&" do not change to "%26".
Solution I tried:
Solution mentioned here
setting encoded=true/false
And also this one.
#DebDeep Asked:
I am passing data in QueryMap as:
private void callCustomFilterSearchPagesApi(String type, ArrayList<FilterListWithHeaderTitle> customFiltersList, int pageNumber, final ApiInteractor listener) {
Map<String, String> queryMap = new HashMap<>();
for (FilterListWithHeaderTitle item: customFiltersList) {
String pairValue;
if (queryMap.containsKey(item.getHeaderTitle())){
// Add the duplicate key and new value onto the previous value
// so (key, value) will now look like (key, value&key=value2)
// which is a hack to work with Retrofit's QueryMap
String oldValue=queryMap.get(item.getHeaderTitle());
String newValue="filters[" + item.getHeaderTitle() + "][]"
+oldValue+ "&"+"filters[" + item.getHeaderTitle() + "][]"+item.getFilterItem();
pairValue=newValue;
}else {
// adding first time
pairValue= item.getFilterItem();
}
try {
//pairValue= URLEncoder.encode(pairValue, "utf-8");
// LoggerUtils.logE(TAG,pairValue);
//queryMap.put(item.getHeaderTitle(), Html.fromHtml(pairValue).toString());
queryMap.put(item.getHeaderTitle(), pairValue);
}catch (Exception u){
LoggerUtils.crashlyticsLog(TAG,u.getMessage());
}
}
Call<JsonObject> call = TagTasteApplicationInitializer.mRetroClient.callCustomFilterApI(type, queryMap, "1");
requestCall(call, listener);
}
Use Interceptor and convert %26 to &:
class RequestInterceptor implements Interceptor {
#Override
Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
String stringurl = request.url().toString();
stringurl = stringurl.replace("%26", "&");
Request newRequest = new Request.Builder()
.url(stringurl)
.build();
return chain.proceed(newRequest);
}
}
Set this to your OkHttp builder:
OkHttpClient client = new OkHttpClient.Builder();
client.addInterceptor(new RequestInterceptor());
I'm trying to get this code block to run but I keep getting a 302. I've tried to show the flow of the code. I just don't know what's wrong.
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.Base64;
public class AuthenticateLoginLogoutExample {
public static void main(String[] args) throws Exception {
new AuthenticateLoginLogoutExample().authenticateLoginLogoutExample(
"http://" + Constants.HOST + "/qcbin",
Constants.DOMAIN,
Constants.PROJECT,
Constants.USERNAME,
Constants.PASSWORD);
}
public void authenticateLoginLogoutExample(final String serverUrl,
final String domain, final String project, String username,
String password) throws Exception {
RestConnector con =
RestConnector.getInstance().init(
new HashMap<String, String>(),
serverUrl,
domain,
project);
AuthenticateLoginLogoutExample example =
new AuthenticateLoginLogoutExample();
//if we're authenticated we'll get a null, otherwise a URL where we should login at (we're not logged in, so we'll get a URL).
It's this next line when it starts on the isAuthenticated() method.
String authenticationPoint = example.isAuthenticated();
Assert.assertTrue("response from isAuthenticated means we're authenticated. that can't be.", authenticationPoint != null);
//do a bunch of other stuff
}
So we go into the isAuthenticated method:
public String isAuthenticated() throws Exception {
String isAuthenticateUrl = con.buildUrl("rest/is-authenticated");
String ret;
Then here on this next line trying to get the response. con.httpGet
Response response = con.httpGet(isAuthenticateUrl, null, null);
int responseCode = response.getStatusCode();
//if already authenticated
if (responseCode == HttpURLConnection.HTTP_OK) {
ret = null;
}
//if not authenticated - get the address where to authenticate
// via WWW-Authenticate
else if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
Iterable<String> authenticationHeader =
response.getResponseHeaders().get("WWW-Authenticate");
String newUrl =
authenticationHeader.iterator().next().split("=")[1];
newUrl = newUrl.replace("\"", "");
newUrl += "/authenticate";
ret = newUrl;
}
//Not ok, not unauthorized. An error, such as 404, or 500
else {
throw response.getFailure();
}
return ret;
}
That jumps us to another class and into this method:
public Response httpGet(String url, String queryString, Map<String,
String> headers)throws Exception {
return doHttp("GET", url, queryString, null, headers, cookies);
}
The doHttp takes us here. type = "GET", url = "http://SERVER/qcbin/rest/is-authenticated", the rest are all empty.
private Response doHttp(
String type,
String url,
String queryString,
byte[] data,
Map<String, String> headers,
Map<String, String> cookies) throws Exception {
if ((queryString != null) && !queryString.isEmpty()) {
url += "?" + queryString;
}
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setRequestMethod(type);
String cookieString = getCookieString();
prepareHttpRequest(con, headers, data, cookieString);
This con.connect() on the next line never connects.
con.connect();
Response ret = retrieveHtmlResponse(con);
updateCookies(ret);
return ret;
}
The prepareHttpRequest code:
private void prepareHttpRequest(
HttpURLConnection con,
Map<String, String> headers,
byte[] bytes,
String cookieString) throws IOException {
String contentType = null;
//attach cookie information if such exists
if ((cookieString != null) && !cookieString.isEmpty()) {
con.setRequestProperty("Cookie", cookieString);
}
//send data from headers
if (headers != null) {
//Skip the content-type header - should only be sent
//if you actually have any content to send. see below.
contentType = headers.remove("Content-Type");
Iterator<Entry<String, String>>
headersIterator = headers.entrySet().iterator();
while (headersIterator.hasNext()) {
Entry<String, String> header = headersIterator.next();
con.setRequestProperty(header.getKey(), header.getValue());
}
}
// If there's data to attach to the request, it's handled here.
// Note that if data exists, we take into account previously removed
// content-type.
if ((bytes != null) && (bytes.length > 0)) {
con.setDoOutput(true);
//warning: if you add content-type header then you MUST send
// information or receive error.
//so only do so if you're writing information...
if (contentType != null) {
con.setRequestProperty("Content-Type", contentType);
}
OutputStream out = con.getOutputStream();
out.write(bytes);
out.flush();
out.close();
}
}
And the getCookieString method:
public String getCookieString() {
StringBuilder sb = new StringBuilder();
if (!cookies.isEmpty()) {
Set<Entry<String, String>> cookieEntries =
cookies.entrySet();
for (Entry<String, String> entry : cookieEntries) {
sb.append(entry.getKey()).append("=").append(entry.getValue()).append(";");
}
}
String ret = sb.toString();
return ret;
}
Does anyone have any idea what went wrong? I don't know why it keeps returning a 302.
EDIT: Added chrome developer image as requested.
I haven't followed your entire code, but http 302 means a redirection
https://en.wikipedia.org/wiki/HTTP_302
Depending on the kind of redirection, that could work smoothly or not. For instance the other day I faced a http to https redirection and I have to solve it checking the location header manually.
What I would do is to check first the headers in the browser, in Chrome go to Developer Tools, Network and check the Response Headers (screenshot). You should see there for a 302 response a Location Header, with the new URL you should follow.
302 means there's a page there, but you really want a different page (or you want this page and then that other page). If you look at the headers you get back from the server when it gives you a 302, you'll probably find a "Location:" header telling you where to query next, and you'll have to write yet another transaction.
Browsers interpret the 302 response and automatically redirect to the URL specified in the "Location:" header.
I am currently using this code to add a custom header to android WebView
Map<String, String> extraHeaders = new HashMap<String, String>();
extraHeaders.put("example", "header");
webView.loadUrl(url, extraHeader);
Above code is working but only on the main page. So if I write this code echo $_SERVER['example'] it prints header. But there is an iframe in the loaded URL which shows an undefined error when I try the same code. Is there any way I can fix this?
So what I want to do is add custom header not only to the main loaded URL but also on the iframe of the loaded page.
This worked for me:
mWebView.setWebViewClient(new MyWebViewClient(token));
in MyWebViewClient
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
try {
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(url.trim())
.addHeader("token", mToken) //add headers
.build();
Response response = httpClient.newCall(request).execute();
return new WebResourceResponse(
getMimeType(url), // set content-type
response.header("content-encoding", "utf-8"),
response.body().byteStream()
);
} catch (IOException e) {
return null;
}
}
//get mime type by url
public String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
if (extension.equals("js")) {
return "text/javascript";
}
else if (extension.equals("woff")) {
return "application/font-woff";
}
else if (extension.equals("woff2")) {
return "application/font-woff2";
}
else if (extension.equals("ttf")) {
return "application/x-font-ttf";
}
else if (extension.equals("eot")) {
return "application/vnd.ms-fontobject";
}
else if (extension.equals("svg")) {
return "image/svg+xml";
}
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}
You can put this setting to your web setting, every request from webview will be using this User-Agent header.
webview.getSettings().setUserAgentString("user-agent-string");
No, that is not possible with Android WebView itself. You have to work around either in your page code, or in your app's code, or on the server.
For fixing this on the page's side, you can use XMLHttpRequest for loading subresources. But for that you will have basically to construct the page on the fly.
On the app's side, you can use WebViewClient.shouldInterceptRequest, to intercept all the network requests. You are not allowed to just modify the provided request, instead, you will need to make a new request yourself, but there you will be able to set any headers you want. See this example: Android WebViewClient url redirection (Android URL loading system)
On the server side, you can look into Referer header of subresources, which must contain the url of the page that has requested it.
Just add this piece of code before load URL:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(null);
}