Retrofit2 使用 RequestBody returns 400 错误请求上传(Android Kotlin MVVM & ASP.NET Core Web API)
Retrofit2 Upload Using RequestBody returns 400 Bad Request (Android Kotlin MVVM & ASP.NET Core Web API)
我不熟悉让 Requestbody 发送表单数据,其中包括从我的 phone 媒体上传图像和一些字符串格式的数据。我在 Postman 中使用过它并且它正在工作,但在我的应用程序中我遇到了问题,即在将数据发布到 API 时它给出了 400 个错误请求。这是我的代码。任何帮助都可以
这是我的后端 api 代码。
[HttpPost]
public ActionResult<FishReadDTO> CreateFish([FromForm] FishCreateDTO fishCreateDTO)
{
var fishmodel = _mapper.Map<Fish>(fishCreateDTO);
if(fishCreateDTO.ImageFile.Length > 0 || fishCreateDTO.ImageFile != null)
{
string apiServerAddress = _httpContextAccessor.HttpContext.Request.Scheme + "://"
+ _httpContextAccessor.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString()
+ ":" + _httpContextAccessor.HttpContext.Connection.LocalPort;
try
{
if(!Directory.Exists(_webHostEnvironment.WebRootPath + "\images\"))
{
Directory.CreateDirectory(_webHostEnvironment.WebRootPath + "\images\");
}
using (FileStream filestream = System.IO.File.Create(_webHostEnvironment.WebRootPath + "\images\" + fishCreateDTO.ImageFile.FileName))
{
fishCreateDTO.ImageFile.CopyTo(filestream);
filestream.Flush();
fishmodel.Image = apiServerAddress + "/images/" + fishCreateDTO.ImageFile.FileName;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error Occured {ex.Message}");
}
}
_repository.CreateFish(fishmodel);
var fishRead = _mapper.Map<FishReadDTO>(fishmodel);
return CreatedAtRoute(nameof(GetFishById), new{Id = fishRead.ID}, fishRead);
}
这是我的 Fish 创建 DTO。
public class FishCreateDTO
{
[Required]
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(100)]
public string Description { get; set; }
[Required]
public float WeightKg { get; set; }
[Required]
public int Stock { get; set; }
[Required]
public float Price { get; set; }
[Required]
public int CategoryID { get; set; }
[Required]
public int UserID { get; set; }
public IFormFile ImageFile {get; set;}
}
前端,我用的是android Kotlin,也涉及到Retrofit2。
这里是 api 调用
@Multipart
@POST("/efishing-api/fish")
suspend fun createFish(
@Part("name") name: String,
@Part("description") description : String,
@Part("weightKG") weightKG: String,
@Part("stock") stock : String,
@Part("price") price : String,
@Part("categoryID") categoryID: String,
@Part("userID") userID : String,
@Part imageFile : MultipartBody.Part
) : FishCreateResponse
这是存储库。
suspend fun createFish(
name : String,
description : String,
weightKG : String,
stock : String,
price : String,
userId: String,
categoryId: String,
imageFile : MultipartBody.Part
) : FishCreateResponse {
return RetrofitBuilder.apiService.createFish(
name,
description,
weightKG,
stock,
price,
categoryId,
userId,
imageFile
)
}
这是我正在使用的视图模型
fun createFish(
name : String,
description : String,
weightKG : String,
stock : String,
price : String,
userId: String,
categoryId: String,
imageFile : MultipartBody.Part
) {
CoroutineScope(IO).launch {
val fishCreate = fishRepository.createFish(
name,
description,
weightKG,
stock,
price,
categoryId,
userId,
imageFile
)
}
}
这是我尝试上传表单数据的地方
val file = File(filePath)
val requestBody = file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
fishViewModel.createFish(
Fishname.text.toString(),
Description.text.toString(),
FishPrice.text.toString(),
Weight.text.toString(),
Stock.text.toString(),
categoryID.toString(),
userId.toString(),
MultipartBody.Part.createFormData("imageFile", file.name, requestBody)
)
这里是logcat
2021-07-20 01:49:31.117 24404-25371/com.example.efishingapp E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-3
Process: com.example.efishingapp, PID: 24404
retrofit2.HttpException: HTTP 400 Bad Request
at retrofit2.KotlinExtensions$await.onResponse(KotlinExtensions.kt:53)
at retrofit2.OkHttpCall.onResponse(OkHttpCall.java:161)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
*更新(我的改造)
@Multipart
@POST("/efishing-api/fish")
suspend fun createFish(
@Part("name") name: String,
@Part("description") description : String,
@Part("weightKG") weightKG: String,
@Part("stock") stock : String,
@Part("price") price : String,
@Part("categoryID") categoryID: String,
@Part("userID") userID : String,
@Part imageFile : MultipartBody.Part
) : FishCreateResponse
任何解决方案都可以。谢谢
感谢您帮助我,但我能够解决它。这是我做的答案
首先,如果您查看了我的 FishCreateDTO 所在的代码,它有一些数据属性。我确保我遵循了 API.
请求的那些数据属性的顺序
其次,我在 Kotlin
中更改了 Api 服务接口 class
@Multipart
@POST("/efishing-api/fish/addfish")
suspend fun createFish(
@Part("name") name: RequestBody,
@Part("description") description : RequestBody,
@Part("weightKG") weightKG: RequestBody,
@Part("stock") stock : RequestBody,
@Part("price") price : RequestBody,
@Part("categoryID") categoryID: RequestBody,
@Part("userID") userID : RequestBody,
@Part imageFile : MultipartBody.Part
) : FishCreateResponse
如果您注意到我从 String 数据类型更改为 RequestBody 数据类型。有人说您仍然可以明确提及数据类型,如 String 或 Int,但为了更安全,请使用 RequestBody。由于我在界面 class 中更改了数据类型,这必须反映到存储库和 ViewModal class 中。要查看这些 class 的原文,请查看我的问题内容。
最后,我更改了 activity 中的代码。
val file = File(filePath)
val requestBody = RequestBody.create("image/*".toMediaTypeOrNull(), file)
Log.e("Post Activity", "Image choosen path $filePath")
fishViewModel.createFish(
Fishname.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
Description.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
Weight.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
Stock.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
FishPrice.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
categoryID.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
userId.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
MultipartBody.Part.createFormData("imageFile", file.name, requestBody)
)
我确保 text/JSON 数据的所有数据类型为“text/plain”,图像为“image/*”。以前,它位于图像的“multipart/form-data”中,因此无法上传图像,从而引发 400 Bad Request 错误。
**注意:有时 api 端点和 API 的基 URL 可能包含空格或其他不需要的字符(@Ghanshyam 提到)但如果它是正确的并且与您的 API 匹配,然后查看您的代码。错误可能存在于您的 activity 或您的视图模型或您的存储库中。它也可以在 Api 接口 class 中。这个答案遵循 Android MVVM code with Kotlin and ASP.NET Core Web API MVC
我不熟悉让 Requestbody 发送表单数据,其中包括从我的 phone 媒体上传图像和一些字符串格式的数据。我在 Postman 中使用过它并且它正在工作,但在我的应用程序中我遇到了问题,即在将数据发布到 API 时它给出了 400 个错误请求。这是我的代码。任何帮助都可以
这是我的后端 api 代码。
[HttpPost]
public ActionResult<FishReadDTO> CreateFish([FromForm] FishCreateDTO fishCreateDTO)
{
var fishmodel = _mapper.Map<Fish>(fishCreateDTO);
if(fishCreateDTO.ImageFile.Length > 0 || fishCreateDTO.ImageFile != null)
{
string apiServerAddress = _httpContextAccessor.HttpContext.Request.Scheme + "://"
+ _httpContextAccessor.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString()
+ ":" + _httpContextAccessor.HttpContext.Connection.LocalPort;
try
{
if(!Directory.Exists(_webHostEnvironment.WebRootPath + "\images\"))
{
Directory.CreateDirectory(_webHostEnvironment.WebRootPath + "\images\");
}
using (FileStream filestream = System.IO.File.Create(_webHostEnvironment.WebRootPath + "\images\" + fishCreateDTO.ImageFile.FileName))
{
fishCreateDTO.ImageFile.CopyTo(filestream);
filestream.Flush();
fishmodel.Image = apiServerAddress + "/images/" + fishCreateDTO.ImageFile.FileName;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error Occured {ex.Message}");
}
}
_repository.CreateFish(fishmodel);
var fishRead = _mapper.Map<FishReadDTO>(fishmodel);
return CreatedAtRoute(nameof(GetFishById), new{Id = fishRead.ID}, fishRead);
}
这是我的 Fish 创建 DTO。
public class FishCreateDTO
{
[Required]
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(100)]
public string Description { get; set; }
[Required]
public float WeightKg { get; set; }
[Required]
public int Stock { get; set; }
[Required]
public float Price { get; set; }
[Required]
public int CategoryID { get; set; }
[Required]
public int UserID { get; set; }
public IFormFile ImageFile {get; set;}
}
前端,我用的是android Kotlin,也涉及到Retrofit2。
这里是 api 调用
@Multipart
@POST("/efishing-api/fish")
suspend fun createFish(
@Part("name") name: String,
@Part("description") description : String,
@Part("weightKG") weightKG: String,
@Part("stock") stock : String,
@Part("price") price : String,
@Part("categoryID") categoryID: String,
@Part("userID") userID : String,
@Part imageFile : MultipartBody.Part
) : FishCreateResponse
这是存储库。
suspend fun createFish(
name : String,
description : String,
weightKG : String,
stock : String,
price : String,
userId: String,
categoryId: String,
imageFile : MultipartBody.Part
) : FishCreateResponse {
return RetrofitBuilder.apiService.createFish(
name,
description,
weightKG,
stock,
price,
categoryId,
userId,
imageFile
)
}
这是我正在使用的视图模型
fun createFish(
name : String,
description : String,
weightKG : String,
stock : String,
price : String,
userId: String,
categoryId: String,
imageFile : MultipartBody.Part
) {
CoroutineScope(IO).launch {
val fishCreate = fishRepository.createFish(
name,
description,
weightKG,
stock,
price,
categoryId,
userId,
imageFile
)
}
}
这是我尝试上传表单数据的地方
val file = File(filePath)
val requestBody = file.asRequestBody("multipart/form-data".toMediaTypeOrNull())
fishViewModel.createFish(
Fishname.text.toString(),
Description.text.toString(),
FishPrice.text.toString(),
Weight.text.toString(),
Stock.text.toString(),
categoryID.toString(),
userId.toString(),
MultipartBody.Part.createFormData("imageFile", file.name, requestBody)
)
这里是logcat
2021-07-20 01:49:31.117 24404-25371/com.example.efishingapp E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-3
Process: com.example.efishingapp, PID: 24404
retrofit2.HttpException: HTTP 400 Bad Request
at retrofit2.KotlinExtensions$await.onResponse(KotlinExtensions.kt:53)
at retrofit2.OkHttpCall.onResponse(OkHttpCall.java:161)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
*更新(我的改造)
@Multipart
@POST("/efishing-api/fish")
suspend fun createFish(
@Part("name") name: String,
@Part("description") description : String,
@Part("weightKG") weightKG: String,
@Part("stock") stock : String,
@Part("price") price : String,
@Part("categoryID") categoryID: String,
@Part("userID") userID : String,
@Part imageFile : MultipartBody.Part
) : FishCreateResponse
任何解决方案都可以。谢谢
感谢您帮助我,但我能够解决它。这是我做的答案
首先,如果您查看了我的 FishCreateDTO 所在的代码,它有一些数据属性。我确保我遵循了 API.
请求的那些数据属性的顺序其次,我在 Kotlin
中更改了 Api 服务接口 class@Multipart
@POST("/efishing-api/fish/addfish")
suspend fun createFish(
@Part("name") name: RequestBody,
@Part("description") description : RequestBody,
@Part("weightKG") weightKG: RequestBody,
@Part("stock") stock : RequestBody,
@Part("price") price : RequestBody,
@Part("categoryID") categoryID: RequestBody,
@Part("userID") userID : RequestBody,
@Part imageFile : MultipartBody.Part
) : FishCreateResponse
如果您注意到我从 String 数据类型更改为 RequestBody 数据类型。有人说您仍然可以明确提及数据类型,如 String 或 Int,但为了更安全,请使用 RequestBody。由于我在界面 class 中更改了数据类型,这必须反映到存储库和 ViewModal class 中。要查看这些 class 的原文,请查看我的问题内容。
最后,我更改了 activity 中的代码。
val file = File(filePath)
val requestBody = RequestBody.create("image/*".toMediaTypeOrNull(), file)
Log.e("Post Activity", "Image choosen path $filePath")
fishViewModel.createFish(
Fishname.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
Description.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
Weight.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
Stock.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
FishPrice.text.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
categoryID.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
userId.toString().toRequestBody("text/plain".toMediaTypeOrNull()),
MultipartBody.Part.createFormData("imageFile", file.name, requestBody)
)
我确保 text/JSON 数据的所有数据类型为“text/plain”,图像为“image/*”。以前,它位于图像的“multipart/form-data”中,因此无法上传图像,从而引发 400 Bad Request 错误。
**注意:有时 api 端点和 API 的基 URL 可能包含空格或其他不需要的字符(@Ghanshyam 提到)但如果它是正确的并且与您的 API 匹配,然后查看您的代码。错误可能存在于您的 activity 或您的视图模型或您的存储库中。它也可以在 Api 接口 class 中。这个答案遵循 Android MVVM code with Kotlin and ASP.NET Core Web API MVC