java.lang.IllegalArgumentException: URL "https:/my url/api/login/" 不包含“{username}”。 (参数#1)

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

我是 android 工作室的新手,我正在使用 java、改造和网络 api(django restframework)进行登录 activity。我收到此错误,java.lang.IllegalArgumentException:URL“https:// my url /api/login/”不包含“{username}”。 (参数#1) 对于方法 UserService.login ,为什么会出现此错误?即使我的网站 api 正常工作?

这是我的post数据

这是我的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, "Username is required", Toast.LENGTH_SHORT).show();
            return false;
        }
        if(password == null || password.trim().length() == 0){
            Toast.makeText(this, "Password is required", 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("true")){
                        //login start main activity
                        Intent intent = new Intent(LoginActivity.this, DestinationListActivity.class);
                        intent.putExtra("username", username);
                        startActivity(intent);

                    } else {
                        Toast.makeText(LoginActivity.this, "The username or password is incorrect", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(LoginActivity.this, "Error! Please try again!", Toast.LENGTH_SHORT).show();
                }
            }

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

我的用户服务

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

}

这是我的 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;
    }
}

我的资源对象

public class ResObj {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

我的 ApiUtils

public class ApiUtils {

    public static final String BASE_URL = "https:// my url /";

    public static UserService getUserService(){
        return RetrofitClient.getClient(BASE_URL).create(UserService.class);
    }
}

这是我收到的完整错误

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.smartherd.globofly, PID: 27242
    java.lang.IllegalArgumentException: URL "https:// my url /api/login/" does not contain "{username}". (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.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.access0(LoginActivity.java:20)
        at com.smartherd.globofly.activities.LoginActivity.onClick(LoginActivity.java:44)
        at android.view.View.performClick(View.java:7125)
        at android.view.View.performClickInternal(View.java:7102)
        at android.view.View.access00(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

更新

对于 @POST 类型的请求,您还可以使用 @Field 来传递表单编码请求的各个字段。

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

@Field 的文档和示例。

要处理响应:

定义一个简单的classAuthenticationResponse:

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() {
        @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("username", username);
                    startActivity(intent);

                } else {
                    Toast.makeText(LoginActivity.this, "The username or password is incorrect", Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(LoginActivity.this, "Error! Please try again!", Toast.LENGTH_SHORT).show();
            }
        }

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

对于 @POST 类型的请求,如果您需要将参数作为请求 body 的一部分,那么您必须使用 @Body 注释对这些参数进行注释。但是您只能使用一个 @Body 注释,因为使用该注释声明的参数将定义请求的整个主体。

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

其中 Credentials 是一个 POJO class:

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 的文档和示例。

您的登录 http 端点是否期望您将登录数据(用户名、密码)作为正文中的 urlencoded 参数发送? 然后尝试像这样修改您的 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);
}