如何使用Retrofit发送POST请求?

3

我正在尝试使用Retrofit发送POST请求,但服务器响应错误400:Bad Request。

基本URL:http://taskdemoserver.pythonanywhere.com

这是服务器文档的截图。

Server doc

这是Retrofit接口。

package com.e.databaseapp

import retrofit2.Call
import retrofit2.http.*

interface Service {
    @Headers("user: joan","Content-Type: application/json")
    @GET( "task" )
    fun getListTask() : Call<TaskListContainer>

    @Headers("user: joan","Content-Type: application/json")
    @POST("task")
    fun saveTask (@Body task: Task) : Call<Task>

    @Headers("user: joan")
    @DELETE("task")
    fun deleteTask (task: String) : Call<Void>
}

我在我的仓库中创建了一个Retrofit实例。

lass TaskRepository(val taskDao: TaskDao) {
    val gson : Gson = GsonBuilder()
        .setLenient()
        .create()

    val loggingInterceptor : HttpLoggingInterceptor = HttpLoggingInterceptor()
        .setLevel(HttpLoggingInterceptor.Level.BODY)

    val httpClient : OkHttpClient = OkHttpClient.Builder()
        .addInterceptor(loggingInterceptor)
        .build()

    val retrofit: Retrofit = Retrofit.Builder()
        .baseUrl("http://taskdemoserver.pythonanywhere.com/")
        .addConverterFactory(GsonConverterFactory.create(gson))
        .client(httpClient)
        .build()

    val service: Service = retrofit.create(Service::class.java)

...

}

当我尝试发布任务时,出现了以下错误:

Error 400: Bad Request


1
请添加您的基本URL或Postman图像。 - Infusion Analysts
1
请添加代码,创建Retrofit服务实例的位置以及执行实际调用的代码位置。 - finki
请问您能否更新您的问题,提供最新版本的代码以及详细描述您的服务器端点和您现在遇到的错误是什么? - dglozano
@dglozano 这是我代码的最新版本。 - Joan Martín
你没有按照我的答案所说的去做。你不应该发送application/json,你的服务器期望以FormUrlEncoded形式接收它。删除@Body注释并在顶部包含注释@FormUrlEncoded。请查看我的更新答案。 - dglozano
3个回答

1

基础URL应该以/结尾

更改.baseUrl("http://taskdemoserver.pythonanywhere.com")

val retrofit: Retrofit = Retrofit.Builder()
        .baseUrl("http://taskdemoserver.pythonanywhere.com")
        .addConverterFactory(GsonConverterFactory.create(gson))
        .client(httpClient)
        .build()

.baseUrl("http://taskdemoserver.pythonanywhere.com/")转化为基础网址。


斜杠属于Retrofit中的基本URL。 - Daniel Spiess
在DanielSpiess的代码中,我看到了这个.baseUrl("http://taskdemoserver.pythonanywhere.com") - Yuri Popiv
我认为在今天的Retrofit版本中,基本URL和接口中斜杠都是必需的。 - User One
是的,您将斜杠添加到了Service中。但i属于基本URL。抱歉,您的解决方案是错误的。https://square.github.io/retrofit/2.x/retrofit/retrofit2/Retrofit.Builder.html - Daniel Spiess
1
@DanielSpiess 应该在基础 URL 中加入斜杠 /。我已更新答案。 - Yuri Popiv

0

可能您遇到了以下问题:

1)服务器无法定义响应内容。尝试添加带有内容类型的标头:@Headers("Content-Type: application/json")

2)您的基本URL末尾缺少/斜杠。


0

虽然我不能百分之百确定这是你的问题,因为你上传的服务器文档不完整,但我相信问题在于服务器要求你使用mime类型application/x-www-form-urlencoded发送任务数据,这就是为什么在你上传的带有服务器文档的图片中,在“创建任务”终点右侧会显示formdata

然而,你正在使用@Body tasks: TaskListContainer,它将新任务的信息作为JSON数据application/json发送。 (@Body注释)

这是你正在做的事情

我使用Postman测试了你的终点,并使用application/json作为请求的Content-Type。这是Retrofit现在正在做的,因为你在Retrofit服务中使用了@Body注释。

Headers wrong Response wrong

你应该做的是这样的

现在,如果我使用 Postman 发送一个 POST 请求,但是使用 form-data 类型,用键为 task 的字段和代表要创建的任务名称的字符串值,我会收到一个成功的响应。

Headers correct Response correct

为了告诉 Retrofit 以这种方式发送 POST 请求,您需要像这样定义您的 saveTask 方法:
    interface Service {
    ...

    @Headers("user: joan")
    @FormUrlEncoded
    @POST("task")
    fun saveTask (@Field("task") taskname: String) : Call<Task>

    ...
}

您可以在官方文档中查看有关@FormUrlEncoded注释的更多信息。

此外,正如@YuriPopiv所说,您的baseUrl缺少最后一个斜杠。尝试像这样设置.baseUrl("http://taskdemoserver.pythonanywhere.com/")或者定义您的端点以前导/开头。

来自文档

基本URL应始终以/结尾。 尾随/确保相对路径的端点值将正确附加到具有路径组件的基础上。


首先,感谢你的回答。 现在服务器响应401错误:未经授权。 - Joan Martín
好的,这是一个全新的错误。这是因为服务器在允许您向该端点发出请求之前需要进行身份验证。每个服务器都有自己的身份验证方法,我不知道您的服务器使用的是哪种方法。它可以是简单的基本身份验证,也可以使用更高级的机制,如JWT令牌。如果您将服务器的详细信息发送给我,我可能能够帮助您。您可以开始查看https://futurestud.io/tutorials/android-basic-authentication-with-retrofit - dglozano
还没有...一旦修复了401错误,又会出现400错误。 - Joan Martín

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接