159

I'm using Retrofit to access a RESTful api. The base url is:

http://api.example.com/service

This is the code for the interface:

public interface ExampleService {
    @Headers("Accept: Application/JSON")
    @POST("/album/featured-albums")
    Call<List<Album>> listFeaturedAlbums();
}

and this is how I send request and receive the responce:

new AsyncTask<Void, Void, Response<List<Album>>>() {

        @Override
        protected Response<List<Album>> doInBackground(Void... params) {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://api.example.com/service")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            ExampleService service = retrofit.create(ExampleService.class);

            try {
                return service.listFeaturedAlbums().execute();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Response<List<Album>> listCall) {
            Log.v("Example", listCall.raw().toString());
        }
    }.execute();

the log that I get is the weird thing:

V/Example﹕ Response{protocol=http/1.1, code=404, message=Not Found, url=http://api.example.com/album/featured-albums}

What's going on here?

Ashkan Sarlak
  • 7,124
  • 6
  • 39
  • 51
  • could you solve the issue? as i am also facing the same issue. and even after i try the correct answer i always get in onfailure – Parth Anjaria Sep 12 '17 at 11:02

1 Answers1

367

Retrofit 2 uses the same rules that an <a href=""> would.

The leading / on your relative URL tells Retrofit that it is an absolute path on the host. Here's an example from a presentation I gave showing this:

enter image description here

Note the incorrect URL which was resolved at the bottom.

By removing the leading /, the URL then becomes relative and will combine with the path segments which are part of the base URL. Corrected in the presentation the final URL is now correct:

enter image description here

In your example you do not have a trailing / on the base URL. You probably want to add one so that relative paths are resolved on top of it rather than as a sibling of it.

Jake Wharton
  • 75,598
  • 23
  • 223
  • 230
  • 27
    I don't really understand why this new type of URL resolution is more beneficial. All you can get from it is hidden errors (like indicated by this question), and counter-intuitive results ( `http://api.example.com/service` + `/album/featured-albums` is not `http://api.example.com/album/featured-albums` ). And this silent error stems from nothing but whether you place the `/` at the end of the base url or at the front of the API URL. Are there any use-cases where this truncation is useful? – EpicPandaForce Sep 21 '15 at 19:21
  • 4
    [Here you have the video of the presentation](https://youtu.be/KIAoQbAu3eA?t=32m19s) – Albert Vila Calvo Oct 21 '15 at 13:49
  • 12
    It makes sense to me after watching the presentation, but it would be great to add an small note to the website. It took me some minutes to find out what was going wrong. – Albert Vila Calvo Oct 21 '15 at 13:51
  • 9
    @JakeWharton how do I `@POST` to the `baseUrl` (without adding any `pathSegment`s? Example: `baseUrl` is `http://api.example.com/album/featured-albums` and I want my `@POST` call to go to `http://api.example.com/album/featured-albums` (same URL as base). `@POST("")` throws a `java.lang.IllegalArgumentException: Missing either @POST URL or @Url parameter.` – ZakTaccardi Mar 29 '16 at 15:42
  • 18
    Use `@Post(".")` – Jake Wharton Mar 31 '16 at 04:25
  • 1
    this didnt work for me version 2.0.0-beta4. It removes the suffix after the hostname no matter what. – i_raqz Mar 17 '17 at 13:26
  • @JakeWharton how to deal with the port number? For instance ```http://api.example.com:433/``` and ```GET("foo/bar")``` lead to ```GET http://api.example.com/foo/bar``` but how to retain the port number? – Leo DroidCoder Jan 27 '22 at 14:47