英文:
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 "https:// my url /api/login/" does not contain "{username}". (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, "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();
}
});
}
}
my UserService
public interface UserService {
@POST("https:// my url /api/login/")
Call login(@Path("username") String username, @Path("password") 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 = "https:// my url /";
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 "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$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("https://www.url.com/api/login/")
Call login(@Field("username") String username, @Field("password") 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("https://www.url.com/api/login/")
Call<AuthenticationResponse> login(@Field("username") String username, @Field("password") String password);
Using AuthenticationResponse
will require an update of your LoginActivity
method 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();
}
});
}
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("https://www.url.com/api/login/")
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("name")
annotation you must provide a placeholder for that path argument in your URL.
Instead of:
@POST("https://www.url.com/api/login/")
Call login(@Path("username") String username, @Path("password") String password);
Your POST call declaration should look like this, so the Retrofit will know where to place these path arguments:
@POST("https://www.url.com/api/login/{username}/{password}")
Call login(@Path("username") String username, @Path("password") 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("https:// my url /api/login/")
Call<ResObj> login(@Field("username") String username, @Field("password") String password);
}
专注分享java语言的经验与见解,让所有开发者获益!
评论