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

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

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

问题

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

  1. public class LoginActivity extends AppCompatActivity {
  2. EditText edtUsername;
  3. EditText edtPassword;
  4. Button btnLogin;
  5. UserService userService;
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_login);
  10. edtUsername = (EditText) findViewById(R.id.edtUsername);
  11. edtPassword = (EditText) findViewById(R.id.edtPassword);
  12. btnLogin = (Button) findViewById(R.id.btnLogin);
  13. userService = ApiUtils.getUserService();
  14. btnLogin.setOnClickListener(new View.OnClickListener() {
  15. @Override
  16. public void onClick(View v) {
  17. String username = edtUsername.getText().toString();
  18. <details>
  19. <summary>英文:</summary>
  20. 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)
  21. for method UserService.login** , why i am getting this error? eventhough my web api is working?
  22. this is my post data
  23. [![enter image description here][1]][1]
  24. [1]: https://i.stack.imgur.com/lq28Q.png
  25. this is my LoginActivity.java
  26. public class LoginActivity extends AppCompatActivity {
  27. EditText edtUsername;
  28. EditText edtPassword;
  29. Button btnLogin;
  30. UserService userService;
  31. @Override
  32. protected void onCreate(Bundle savedInstanceState) {
  33. super.onCreate(savedInstanceState);
  34. setContentView(R.layout.activity_login);
  35. edtUsername = (EditText) findViewById(R.id.edtUsername);
  36. edtPassword = (EditText) findViewById(R.id.edtPassword);
  37. btnLogin = (Button) findViewById(R.id.btnLogin);
  38. userService = ApiUtils.getUserService();
  39. btnLogin.setOnClickListener(new View.OnClickListener() {
  40. @Override
  41. public void onClick(View v) {
  42. String username = edtUsername.getText().toString();
  43. String password = edtPassword.getText().toString();
  44. //validate form
  45. if(validateLogin(username, password)){
  46. //do login
  47. doLogin(username, password);
  48. }
  49. }
  50. });
  51. }
  52. private boolean validateLogin(String username, String password){
  53. if(username == null || username.trim().length() == 0){
  54. Toast.makeText(this, &quot;Username is required&quot;, Toast.LENGTH_SHORT).show();
  55. return false;
  56. }
  57. if(password == null || password.trim().length() == 0){
  58. Toast.makeText(this, &quot;Password is required&quot;, Toast.LENGTH_SHORT).show();
  59. return false;
  60. }
  61. return true;
  62. }
  63. private void doLogin(final String username,final String password){
  64. Call call = userService.login(username,password);
  65. call.enqueue(new Callback() {
  66. @Override
  67. public void onResponse(Call call, Response response) {
  68. if(response.isSuccessful()){
  69. ResObj resObj = (ResObj) response.body();
  70. if(resObj.getMessage().equals(&quot;true&quot;)){
  71. //login start main activity
  72. Intent intent = new Intent(LoginActivity.this, DestinationListActivity.class);
  73. intent.putExtra(&quot;username&quot;, username);
  74. startActivity(intent);
  75. } else {
  76. Toast.makeText(LoginActivity.this, &quot;The username or password is incorrect&quot;, Toast.LENGTH_SHORT).show();
  77. }
  78. } else {
  79. Toast.makeText(LoginActivity.this, &quot;Error! Please try again!&quot;, Toast.LENGTH_SHORT).show();
  80. }
  81. }
  82. @Override
  83. public void onFailure(Call call, Throwable t) {
  84. Toast.makeText(LoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
  85. }
  86. });
  87. }
  88. }
  89. my UserService
  90. public interface UserService {
  91. @POST(&quot;https:// my url /api/login/&quot;)
  92. Call login(@Path(&quot;username&quot;) String username, @Path(&quot;password&quot;) String password);
  93. }
  94. this is my RetrofitClient
  95. public class RetrofitClient {
  96. private static Retrofit retrofit = null;
  97. public static Retrofit getClient(String url){
  98. if(retrofit == null){
  99. retrofit = new Retrofit.Builder()
  100. .baseUrl(url)
  101. .addConverterFactory(GsonConverterFactory.create())
  102. .build();
  103. }
  104. return retrofit;
  105. }
  106. }
  107. my ResObj
  108. public class ResObj {
  109. private String message;
  110. public String getMessage() {
  111. return message;
  112. }
  113. public void setMessage(String message) {
  114. this.message = message;
  115. }
  116. }
  117. my ApiUtils
  118. public class ApiUtils {
  119. public static final String BASE_URL = &quot;https:// my url /&quot;;
  120. public static UserService getUserService(){
  121. return RetrofitClient.getClient(BASE_URL).create(UserService.class);
  122. }
  123. }
  124. this is my full error I receive
  125. E/AndroidRuntime: FATAL EXCEPTION: main
  126. Process: com.smartherd.globofly, PID: 27242
  127. java.lang.IllegalArgumentException: URL &quot;https:// my url /api/login/&quot; does not contain &quot;{username}&quot;. (parameter #1)
  128. for method UserService.login
  129. at retrofit2.Utils.methodError(Utils.java:52)
  130. at retrofit2.Utils.methodError(Utils.java:42)
  131. at retrofit2.Utils.parameterError(Utils.java:61)
  132. at retrofit2.RequestFactory$Builder.validatePathName(RequestFactory.java:732)
  133. at retrofit2.RequestFactory$Builder.parseParameterAnnotation(RequestFactory.java:375)
  134. at retrofit2.RequestFactory$Builder.parseParameter(RequestFactory.java:295)
  135. at retrofit2.RequestFactory$Builder.build(RequestFactory.java:182)
  136. at retrofit2.RequestFactory.parseAnnotations(RequestFactory.java:65)
  137. at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:25)
  138. at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:168)
  139. at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
  140. at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
  141. at $Proxy3.login(Unknown Source)
  142. at com.smartherd.globofly.activities.LoginActivity.doLogin(LoginActivity.java:64)
  143. at com.smartherd.globofly.activities.LoginActivity.access$100(LoginActivity.java:20)
  144. at com.smartherd.globofly.activities.LoginActivity$1.onClick(LoginActivity.java:44)
  145. at android.view.View.performClick(View.java:7125)
  146. at android.view.View.performClickInternal(View.java:7102)
  147. at android.view.View.access$3500(View.java:801)
  148. at android.view.View$PerformClick.run(View.java:27336)
  149. at android.os.Handler.handleCallback(Handler.java:883)
  150. at android.os.Handler.dispatchMessage(Handler.java:100)
  151. at android.os.Looper.loop(Looper.java:214)
  152. at android.app.ActivityThread.main(ActivityThread.java:7356)
  153. at java.lang.reflect.Method.invoke(Native Method)
  154. at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
  155. at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
  156. I/Process: Sending signal. PID: 27242 SIG: 9
  157. </details>
  158. # 答案1
  159. **得分**: 1
  160. ### 更新
  161. 对于 `@POST` 类型的请求你还可以使用 `@Field` 来传递表单编码请求的单个字段
  162. ```java
  163. @FormUrlEncoded
  164. @POST("https://www.url.com/api/login/")
  165. Call login(@Field("username") String username, @Field("password") String password);

具有示例的 @Field 文档。

处理响应:

定义一个简单的类 AuthenticationResponse

  1. public class AuthenticationResponse {
  2. public String token;
  3. }

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

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

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

  1. private void doLogin(final String username, final String password) {
  2. Call<AuthenticationResponse> call = userService.login(username, password);
  3. call.enqueue(new Callback<AuthenticationResponse>() {
  4. @Override
  5. public void onResponse(Call<AuthenticationResponse> call, Response<AuthenticationResponse> response) {
  6. if (response.isSuccessful()) {
  7. AuthenticationResponse authResponse = response.body();
  8. if (!TextUtils.isEmpty(authResponse.token)) {
  9. // 登录开始主活动
  10. Intent intent = new Intent(LoginActivity.this, DestinationListActivity.class);
  11. intent.putExtra("username", username);
  12. startActivity(intent);
  13. } else {
  14. Toast.makeText(LoginActivity.this, "用户名或密码不正确", Toast.LENGTH_SHORT).show();
  15. }
  16. } else {
  17. Toast.makeText(LoginActivity.this, "错误!请重试!", Toast.LENGTH_SHORT).show();
  18. }
  19. }
  20. @Override
  21. public void onFailure(Call<AuthenticationResponse> call, Throwable t) {
  22. Toast.makeText(LoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
  23. }
  24. });
  25. }

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

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

其中 Credentials 是一个POJO类:

  1. class Credentials {
  2. public String username;
  3. public String password;
  4. }

具有示例的 @Body 文档。

为什么会出现错误?

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

不要这样写:

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

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

  1. @POST("https://www.url.com/api/login/{username}/{password}")
  2. 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.

  1. @FormUrlEncoded
  2. @POST(&quot;https://www.url.com/api/login/&quot;)
  3. 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:

  1. public class AuthenticationResponse {
  2. public String token;
  3. }

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

  1. @FormUrlEncoded
  2. @POST(&quot;https://www.url.com/api/login/&quot;)
  3. 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:

  1. private void doLogin(final String username, final String password) {
  2. Call&lt;AuthenticationResponse&gt; call = userService.login(username, password);
  3. call.enqueue(new Callback() {
  4. @Override
  5. public void onResponse(Call call, Response response) {
  6. if (response.isSuccessful()) {
  7. AuthenticationResponse authResponse = (AuthenticationResponse) response.body();
  8. if (!TextUtils.isEmpty(authResponse.token)) {
  9. //login start main activity
  10. Intent intent = new Intent(LoginActivity.this, DestinationListActivity.class);
  11. intent.putExtra(&quot;username&quot;, username);
  12. startActivity(intent);
  13. } else {
  14. Toast.makeText(LoginActivity.this, &quot;The username or password is incorrect&quot;, Toast.LENGTH_SHORT).show();
  15. }
  16. } else {
  17. Toast.makeText(LoginActivity.this, &quot;Error! Please try again!&quot;, Toast.LENGTH_SHORT).show();
  18. }
  19. }
  20. @Override
  21. public void onFailure(Call call, Throwable t) {
  22. Toast.makeText(LoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
  23. }
  24. });
  25. }

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.

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

Where Credentials is a POJO class:

  1. class Credentials {
  2. public String username;
  3. public String password;
  4. }

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:

  1. @POST(&quot;https://www.url.com/api/login/&quot;)
  2. 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:

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

Documentation of @Path with examples.

答案2

得分: 0

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

  1. public class ResObj {
  2. private String token;
  3. public String getToken() {
  4. return token;
  5. }
  6. public void setToken(String token) {
  7. this.token = token;
  8. }
  9. }
  10. public interface UserService {
  11. @FormUrlEncoded
  12. @POST("https:// my url /api/login/")
  13. Call<ResObj> login(@Field("username") String username, @Field("password") String password);
  14. }
英文:

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:

  1. public class ResObj {
  2. private String token;
  3. public String getToken() {
  4. return token;
  5. }
  6. public void setToken(String token) {
  7. this.token = token;
  8. }
  9. }
  10. public interface UserService {
  11. @FormUrlEncoded
  12. @POST(&quot;https:// my url /api/login/&quot;)
  13. Call&lt;ResObj&gt; login(@Field(&quot;username&quot;) String username, @Field(&quot;password&quot;) String password);
  14. }

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:

确定