Can retrofit get json file from assets folder? - android

Code
public interface LocalApi {
String HOST = "file:///android_asset/";
#GET("{filename}")
Flowable<XXXXBean> getLocalData(#Path("filename") String filename);
}
but I get a NullPointExpection, why?

Internally Retrofit uses OkHttp's HttpUrl class to figure out what the actual URL will be. This class is designed to only work for the http:// and https:// schemes. This means that it cannot retrieve local files and will either throw an Exception or produce a result you don't expect.

Related

How to hide API URL and parameters in Android APP?

I'm curious to know that without commercial product for obfuscation, is there any way where I can store API url and parameters safely which cannot be compiled in reverse engineering? I have tried all my apps and their API url and code is easy to read. I'm concerned about security.
Hide Url in Environmental variables,BuildConfig and Android Studio
One simple way to avoid this bad practice is to store your values
inside an environmental variable, so only your machine knows it, then
read this values in some way and inject them in your code at build
time. Let’s see how to do that using Android Studio, Gradle, and
BuildConfig.
First, we need to create these environmental vars. In Linux and Mac,
create or edit the file ~/.gradle/gradle.properties (pay attention to
the actual Gradle User Home directory position) and add some values:
WEBServiceBaseURL="http://192.168.2.102:2323/"
WEBServiceBaseSMSURL="https://www.example.com/"
Second, in your module’s build.gradle file, add these lines
//Add these lines
def Base_URL = '"' + WEBServiceBaseURL + '"' ?: '"Define BASE URL"';
def SMS_Base_URL = '"' + WEBServiceBaseSMSURL + '"' ?: '"Define SMS BASE URL"';
android.buildTypes.each { type ->
type.buildConfigField 'String', 'Base_URL', WEBServiceBaseURL
type.buildConfigField 'String', 'SMS_Base_URL', WEBServiceBaseSMSURL
}
Use in Java File Like
BuildConfig.Base_URL it will return URL String
public static Retrofit getClient() {
if (retrofit==null) {
retrofit =new Retrofit.Builder()
.baseUrl(BuildConfig.Base_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
I found a solution to hide base url to keep api secured with NDK. Keep base64 encoded string inside cpp file and call that from java class and decode base64.
Include c++ (NDK) support to your project. You can include this to your new or old project.
Your cpp file name can be like (native-lib.cpp)
Search online base64 encoder and encode your base url. Now keep encoded string inside cpp file
Inside cpp file sample code is like:
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_touhidapps_MyProject_utils_MyConstants_baseUrlFromJNI(JNIEnv *env, jobject) {
std::string mUrl = "aHR0cDovL2FwaS5leGFtcGxlLmNvbS8="; //"http://api.example.com/";
return env->NewStringUTF(mUrl.c_str());
}
Inside MyConstants.java class: (where I kept all api urls.)
// load c++ library
static {
System.loadLibrary("native-lib");
}
public static native String baseUrlFromJNI();
// decode base64 to a string and get normal url
public static String getSecureBaseUrl() {
String mUrl = baseUrlFromJNI();
try {
String text = new String(Base64.decode(mUrl, Base64.DEFAULT), "UTF-8");
return text;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
mUrl = "http://demo.example.com/"; // don't change this link. This will not execute normally, if exception happens then it will return a demo url.
return mUrl;
}
Now you can get your original url like below:
public static final String API_BASE = "" + getSecureBaseUrl();
Your question is not ideal for the StackOverflow as the topic is too broad and primarily opinion based. However, I thought I can share some of my thoughts as an answer here.
Hiding API urls with code obfuscation is definitely a good idea and it may work in some cases as well if you want to hide those. You might consider encrypting the API url in your code as well and store the encrypted url in your SharedPreferences or in local storage which needs to be decrypted again each time when you're using your API url to call a web service.
But none of these can't ensure that your API urls are uncrackable. If someone really wants to get your API urls s/he can easily get those by tracking the network that you're using to call the web services.
So encrypting API urls and obfuscating the variable names to hide the API urls will not work in most of the cases as you expected. And yes, I don't see any security breach in getting your API urls either. Because, the API server should be designed in a way that it can block unwanted service calls an attacker is making through an API. You might consider thinking of setting up a firewall in your host machines or can setup a basic authentication protocol which will protect your data. There are a lot of ways to prevent these security breach activities. You might also consider reading this article which I found useful to get a heads-up on how you can protect your APIs to be abused.
Hope that helps.

How to detect a redirection in Glide v4 / OkHttp3?

Using Glide v4 and OkHttp3, how can I detect a redirection and load another url when it happens?
My usecase: I use the Glide v4 library with OkHttp3 to download pictures in my app. Sometimes when a picture is not available, a redirection is performed by the server to provide another picture instead of the one I originaly wanted. I can see it in my browser because when I request url A, I finally land on url B with the second picture. When that happens I want to instead load url C that is derived from url A (so not a static url).
At the moment I can detect the redirection using an OkHttp3 Interceptor:
public Response intercept(#NonNull Chain chain) throws IOException {
String requestUrl = chain.request().url().toString();
Response response = chain.proceed(chain.request());
String responseUrl = response.request().url().toString();
boolean redirect = !requestUrl.equals(responseUrl);
if (redirect) {
Timber.d("Detected redirection");
}
return response;
}
but then I don't know how to cleanly load url C. I don't see how I can load another url in the interceptor, and if I throw an IOException to handle the error later in a Glide RequestListener it will just result in a GlideException so I can't determine why it was throw.
OkHttp should be automatically redirecting by default and loading the final content. See OkHttpClient.Builder.followRedirects.
https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.Builder.html#followRedirects-boolean-
My own testing suggests this is working, e.g.
$ oksocial 'https://httpbin.org/redirect-to?url=https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg'
For detecting it, I assume your interceptor is registered via addInterceptor instead of addNetworkInterceptor. Is that correct?
It is also worth ensuring the redirect is via a 30X response and not via a HTML refresh.
The solution I ended up with is pretty ugly: the request interceptor I showed in the question raise an IOException when a redirection is detected. I will then use a GlideListener to detect the error, and load the url C. The ugly part is that in the GlideListener I can't determine the cause of the error, it can be a redirection but it can also be a network error or anything else.
A cleaner version of this solution can probably be achieved with a custom OkHttpUrlLoader but it's too much code for my simple usecase.
This is my solution in Glide v4 / OkHttp3
String redirect = response.header("location");
if(redirect != null && !request.url().toString().equals(redirect)) {
// if redirected your code here
}

java.lang.IllegalArgumentException: Illegal URL with retrofit

i'm trying to call an api in my application
i've the following url template
test-test.domainname.com/feeds/json/v3/attribute/attribute
i'm using retrofit 2
but i get the following fatal exception
Illegal URL: test-test.domainname.com
and this is my interface
public interface Iinterface{
#GET("feeds/json/v3/attribute/"+attribute)
Call<ArrayList<result>>getresult();
}
can someone help me with this problem ...
my base URL is here: http://myapiname.azurewebservices.net
and feed method is like that :
public interface Iinterface{
#GET("/feeds/json/v3/attribute/"+attribute)
Call<ArrayList<result>>getresult();
}
And working perfectly. Please add http or https and try again
You do not have a protocol section. Prepend http:// or https:// depending on which applies to your url --
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://test-test.domainname.com")
// ... other retrofit options
.build();
In my case,
my base url contained space character. (eg. http://myapiname.azure webservices.net )
I fixed this Error by removing space in my base URL.
Illegal URL Exception in retrofit is triggered when your passed url is
not really existed or not fix with url standard.
By mistake, I provided empty string to baseUrl() so because of this i was getting java.lang.IllegalArgumentException: Illegal URL exception.

Android http post send with different charset encoding

i've got a problem do a http post on android with german "umlaute" äöü. I am passing a json object to the method below and execute the returned ClientResource with post and the request entity in the returned client response. When I want to post something like { "foo":"bär" } the HttpClient sends something like { "foo":"b√§r" }.
Don't know why. What am I doing wrong.
public static ClientResource newPostRequest(Context context, String urn,
JSONObject form) throws MissingAccessTokenException {
ClientResource resource = new ClientResource(uri + urn);
StringRepresentation sr = new StringRepresentation(form.toString());
sr.setMediaType(MediaType.APPLICATION_JSON);
resource.getRequest().setEntity(sr);
return resource;
}
Update
I used the default android http client (which is a apache http client I believe) and got the same error. So the problem might be located here. I try to implement another json parser (currently gson) and (if possible) another http client. Be back later...
Update
Gson is not the problem. I added a json String to the StringRepresentation and nothing changed.
ANSWER
Well that's an odd one. Maybe someone can clear this for me. I always asked myself, why √§ where used and I figured out that translating the utf-8 ä leads to √§. Obviously my android phone did not use macroman, but my mac did. I therefor changed the text file encoding in eclipse, restarted eclipse and the tomcat server and it worked. Still the TCP/IP Monitor in eclipse uses mac roman which looks still wrong. It was thereby a problem with my server, not with the restlet client on android. I just couldn't see it because the TCP/IP Monitor encoded everything in macroman.
did you try calling setCharacterSet(...) on your StringRepresentation? e.g.,
StringRepresentation sr = new StringRepresentation(form.toString());
sr.setCharacterSet(CharacterSet.UTF_8);

How can I use http auth information from a URL in Android?

In my browser, or in iOS, when I try to get the contents of a URL with encoded http authentication information in the form
http://myUser:myPassword#www.example.com/secure/area/index.html
It just works. I'm getting URLs from a web service, and I'd like to avoid trying to parse them up for their HTTP auth info if I can help it. Is there a way to do something similar in Android without actually parsing the URLs? Alternatively, what is the best way to go about that?
UPDATE:
I find that when I try to set the authentication information in an Authorization header, I get a very strange FileNotFoundException.
Here's the code I'm using:
URL url = new URL(urlString);
URLConnection connection;
String authority = url.getAuthority();
if (authority.contains("#")) {
String userPasswordString = authority.split("#")[0];
url = new URL(urlString.replace(userPasswordString + "#", ""));
connection = url.openConnection();
String encoded = new String(Base64.encode(userPasswordString.getBytes(), Base64.DEFAULT), "UTF-8");
connection.setRequestProperty("Authorization", "Basic " + encoded);
} else {
connection = url.openConnection();
}
InputStream responseStream = connection.getInputStream();
All the info seems to check out, I've verified the url is correct, the base64 string is correct, and the file is certainly on the server--I have no trouble at all opening it with Firefox, and Firebug shows all the right headers, matching what I've sent as far as I can tell. What I get though is the following error (url host changed to protect the innocent):
java.io.FileNotFoundException: http://a1b.example.com/grid/uploads/profile/avatar/user1/custom-avatar.jpg
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1061)
Any idea what this is all about?
I looked into using HttpClient, but saw that in Issue 16041 it is recommended that we prefer URLConnection.
That looks like your browser is applying some extra rules to parsing the URL. In Android you can use HTTP Client's authentication mechanism such as BASIC and DIGEST to do the same things. Which one you choose is dependent on the server you are trying to authenticate against.
Here is a good page to get you started.
Unfortunately, on Android you can't pass the user info (username/password) in that format to either java.net.URL or HttpClient and have it work like in a browser.
I'd recommend using URI (see http://download.oracle.com/javase/1.5.0/docs/api/index.html?java/net/URI.html) to do this: pass your URL to the URI constructor that takes a String and then you can extract the user info (using getUserInfo()). You can then either use HttpClient's authorization classes (see http://developer.android.com/reference/org/apache/http/auth/package-summary.html) or build the basic auth header yourself (an example is given at http://www.avajava.com/tutorials/lessons/how-do-i-connect-to-a-url-using-basic-authentication.html).

Categories

Resources