java.lang.IllegalArgumentException: URL "https:/my url/api/login/" does not contain "{username}". (parameter #1)

huangapple go评论69阅读模式
英文:

java.lang.IllegalArgumentException: URL "https:/my url/api/login/" does not contain "{username}". (parameter #1)

问题

以下是你提供的代码的翻译部分:

public class LoginActivity extends AppCompatActivity {
    EditText edtUsername;
    EditText edtPassword;
    Button btnLogin;
    UserService userService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        edtUsername = (EditText) findViewById(R.id.edtUsername);
        edtPassword = (EditText) findViewById(R.id.edtPassword);
        btnLogin = (Button) findViewById(R.id.btnLogin);
        userService = ApiUtils.getUserService();

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = edtUsername.getText().toString();
               

<details>
<summary>英文:</summary>

I am new in android studio, and i am doing login activity using java, retrofit and web api (django restframework). i am getting this error, **java.lang.IllegalArgumentException: URL &quot;https:// my url /api/login/&quot; does not contain &quot;{username}&quot;. (parameter #1)
        for method UserService.login** , why i am getting this error? eventhough my web api is working?

this is my post data

[![enter image description here][1]][1]


  [1]: https://i.stack.imgur.com/lq28Q.png

this is my LoginActivity.java

    public class LoginActivity extends AppCompatActivity {
        EditText edtUsername;
        EditText edtPassword;
        Button btnLogin;
        UserService userService;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_login);
    
            edtUsername = (EditText) findViewById(R.id.edtUsername);
            edtPassword = (EditText) findViewById(R.id.edtPassword);
            btnLogin = (Button) findViewById(R.id.btnLogin);
            userService = ApiUtils.getUserService();
    
            btnLogin.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String username = edtUsername.getText().toString();
                    String password = edtPassword.getText().toString();
                    //validate form
                    if(validateLogin(username, password)){
                        //do login
                        doLogin(username, password);
                    }
                }
            });
    
        }
    
        private boolean validateLogin(String username, String password){
            if(username == null || username.trim().length() == 0){
                Toast.makeText(this, &quot;Username is required&quot;, Toast.LENGTH_SHORT).show();
                return false;
            }
            if(password == null || password.trim().length() == 0){
                Toast.makeText(this, &quot;Password is required&quot;, Toast.LENGTH_SHORT).show();
                return false;
            }
            return true;
        }
    
        private void doLogin(final String username,final String password){
            Call call = userService.login(username,password);
            call.enqueue(new Callback() {
                @Override
                public void onResponse(Call call, Response response) {
                    if(response.isSuccessful()){
                        ResObj resObj = (ResObj) response.body();
                        if(resObj.getMessage().equals(&quot;true&quot;)){
                            //login start main activity
                            Intent intent = new Intent(LoginActivity.this, DestinationListActivity.class);
                            intent.putExtra(&quot;username&quot;, username);
                            startActivity(intent);
    
                        } else {
                            Toast.makeText(LoginActivity.this, &quot;The username or password is incorrect&quot;, Toast.LENGTH_SHORT).show();
                        }
                    } else {
                        Toast.makeText(LoginActivity.this, &quot;Error! Please try again!&quot;, Toast.LENGTH_SHORT).show();
                    }
                }
    
                @Override
                public void onFailure(Call call, Throwable t) {
                    Toast.makeText(LoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

my UserService

    public interface UserService {
        @POST(&quot;https:// my url /api/login/&quot;)
        Call login(@Path(&quot;username&quot;) String username, @Path(&quot;password&quot;) String password);
    
    }

this is my RetrofitClient

    public class RetrofitClient {
        private static Retrofit retrofit = null;
    
        public static Retrofit getClient(String url){
            if(retrofit == null){
                retrofit = new Retrofit.Builder()
                        .baseUrl(url)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();
            }
            return retrofit;
        }
    }

my ResObj

    public class ResObj {
        private String message;
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }

my ApiUtils

    public class ApiUtils {
    
        public static final String BASE_URL = &quot;https:// my url /&quot;;
    
        public static UserService getUserService(){
            return RetrofitClient.getClient(BASE_URL).create(UserService.class);
        }
    }

this is my full error I receive

    E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.smartherd.globofly, PID: 27242
        java.lang.IllegalArgumentException: URL &quot;https:// my url /api/login/&quot; does not contain &quot;{username}&quot;. (parameter #1)
            for method UserService.login
            at retrofit2.Utils.methodError(Utils.java:52)
            at retrofit2.Utils.methodError(Utils.java:42)
            at retrofit2.Utils.parameterError(Utils.java:61)
            at retrofit2.RequestFactory$Builder.validatePathName(RequestFactory.java:732)
            at retrofit2.RequestFactory$Builder.parseParameterAnnotation(RequestFactory.java:375)
            at retrofit2.RequestFactory$Builder.parseParameter(RequestFactory.java:295)
            at retrofit2.RequestFactory$Builder.build(RequestFactory.java:182)
            at retrofit2.RequestFactory.parseAnnotations(RequestFactory.java:65)
            at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:25)
            at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:168)
            at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
            at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
            at $Proxy3.login(Unknown Source)
            at com.smartherd.globofly.activities.LoginActivity.doLogin(LoginActivity.java:64)
            at com.smartherd.globofly.activities.LoginActivity.access$100(LoginActivity.java:20)
            at com.smartherd.globofly.activities.LoginActivity$1.onClick(LoginActivity.java:44)
            at android.view.View.performClick(View.java:7125)
            at android.view.View.performClickInternal(View.java:7102)
            at android.view.View.access$3500(View.java:801)
            at android.view.View$PerformClick.run(View.java:27336)
            at android.os.Handler.handleCallback(Handler.java:883)
            at android.os.Handler.dispatchMessage(Handler.java:100)
            at android.os.Looper.loop(Looper.java:214)
            at android.app.ActivityThread.main(ActivityThread.java:7356)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
    I/Process: Sending signal. PID: 27242 SIG: 9



</details>


# 答案1
**得分**: 1

### 更新
对于 `@POST` 类型的请求你还可以使用 `@Field` 来传递表单编码请求的单个字段

```java
@FormUrlEncoded
@POST("https://www.url.com/api/login/")
Call login(@Field("username") String username, @Field("password") String password);

具有示例的 @Field 文档。

处理响应:

定义一个简单的类 AuthenticationResponse

public class AuthenticationResponse {
    public String token;
}

然后将其用作你的 login 调用的泛型类型参数:

@FormUrlEncoded
@POST("https://www.url.com/api/login/")
Call<AuthenticationResponse> login(@Field("username") String username, @Field("password") String password);

使用 AuthenticationResponse 将需要更新你的 LoginActivity 方法 doLogin

private void doLogin(final String username, final String password) {
    Call<AuthenticationResponse> call = userService.login(username, password);
    call.enqueue(new Callback<AuthenticationResponse>() {
        @Override
        public void onResponse(Call<AuthenticationResponse> call, Response<AuthenticationResponse> response) {
            if (response.isSuccessful()) {
                AuthenticationResponse authResponse = response.body();
                if (!TextUtils.isEmpty(authResponse.token)) {
                    // 登录开始主活动
                    Intent intent = new Intent(LoginActivity.this, DestinationListActivity.class);
                    intent.putExtra("username", username);
                    startActivity(intent);

                } else {
                    Toast.makeText(LoginActivity.this, "用户名或密码不正确", Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(LoginActivity.this, "错误!请重试!", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onFailure(Call<AuthenticationResponse> call, Throwable t) {
            Toast.makeText(LoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
        }
    });
}

对于需要将参数作为请求的 主体@POST 类型的请求,你必须使用 @Body 注解对这些参数进行标注。但是你只能使用一个 @Body 注解,因为使用该注解声明的参数将定义整个请求的主体。

@POST("https://www.url.com/api/login/")
Call login(@Body Credentials credentials);

其中 Credentials 是一个POJO类:

class Credentials {
    public String username;
    public String password;
}

具有示例的 @Body 文档。

为什么会出现错误?

当你使用 @Path("name") 注解时,你必须为URL中的该路径参数提供占位符。

不要这样写:

@POST("https://www.url.com/api/login/")
Call login(@Path("username") String username, @Path("password") String password);

你的 POST 调用声明应该像这样,这样 Retrofit 就知道在哪里放置这些路径参数:

@POST("https://www.url.com/api/login/{username}/{password}")
Call login(@Path("username") String username, @Path("password") String password);

具有示例的 @Path 文档。

英文:

Update

For @POST type of requests, you can also use @Field to pass individual fields of form-encoded request.

@FormUrlEncoded
@POST(&quot;https://www.url.com/api/login/&quot;)
Call login(@Field(&quot;username&quot;) String username, @Field(&quot;password&quot;) String password);

Documentation of @Field with examples.

To process the response:

Define a simple class AuthenticationResponse:

public class AuthenticationResponse {
public String token;
}

And use it as a generic type parameter of your login call:

@FormUrlEncoded
@POST(&quot;https://www.url.com/api/login/&quot;)
Call&lt;AuthenticationResponse&gt; login(@Field(&quot;username&quot;) String username, @Field(&quot;password&quot;) String password);

Using AuthenticationResponse will require an update of your LoginActivity method doLogin:

private void doLogin(final String username, final String password) {
Call&lt;AuthenticationResponse&gt; call = userService.login(username, password);
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
if (response.isSuccessful()) {
AuthenticationResponse authResponse = (AuthenticationResponse) response.body();
if (!TextUtils.isEmpty(authResponse.token)) {
//login start main activity
Intent intent = new Intent(LoginActivity.this, DestinationListActivity.class);
intent.putExtra(&quot;username&quot;, username);
startActivity(intent);
} else {
Toast.makeText(LoginActivity.this, &quot;The username or password is incorrect&quot;, Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(LoginActivity.this, &quot;Error! Please try again!&quot;, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call call, Throwable t) {
Toast.makeText(LoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}

For @POST type of requests if you need your parameters to be part of the request body then you must annotate those parameters with @Body annotation. But you can you only one @Body annotation as the parameter declared with that annotation will define the whole body of a request.

@POST(&quot;https://www.url.com/api/login/&quot;)
Call login(@Body Credentials credentials);

Where Credentials is a POJO class:

class Credentials {
public String username;
public String password;
}

Documentation of @Body.

Why exactly error happens?

When you use @Path(&quot;name&quot;) annotation you must provide a placeholder for that path argument in your URL.

Instead of:

@POST(&quot;https://www.url.com/api/login/&quot;)
Call login(@Path(&quot;username&quot;) String username, @Path(&quot;password&quot;) String password);

Your POST call declaration should look like this, so the Retrofit will know where to place these path arguments:

@POST(&quot;https://www.url.com/api/login/{username}/{password}&quot;)
Call login(@Path(&quot;username&quot;) String username, @Path(&quot;password&quot;) String password);

Documentation of @Path with examples.

答案2

得分: 0

你的登录 http 终端点是否期望你将登录数据(用户名、密码)作为 url 编码参数发送到请求体中?
如果是的话,尝试像这样修改你的 UserService 接口:

public class ResObj {
private String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
public interface UserService {
@FormUrlEncoded
@POST("https:// my url /api/login/")
Call<ResObj> login(@Field("username") String username, @Field("password") String password);
}
英文:

Is your login http endpoint expecting, that you're sending the login data (username, password) as urlencoded parameters in the body?
Then try to modify your UserService interface like this:

public class ResObj {
private String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
public interface UserService {
@FormUrlEncoded
@POST(&quot;https:// my url /api/login/&quot;)
Call&lt;ResObj&gt; login(@Field(&quot;username&quot;) String username, @Field(&quot;password&quot;) String password);
}

huangapple
  • 本文由 发表于 2020年7月24日 15:09:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/63068557.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定