I am new to making Requestbody to send form data which consists of uploading an image from my phone media and some data which are in string format. I have used it in Postman and it's working but in my app I am facing problem where it gives 400 bad request when posting data to API. Here is my codes. Any help would do
This is my backend api code.
[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);
}
Here is my Fish Create 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;}
}
For the front-end, I am using android Kotlin which also involves with Retrofit2.
Here is the api call
@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
Here is the repository.
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
)
}
Here is the viewmodel that I am using
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
)
}
}
Here is where I am trying to upload my form data
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)
)
Here is the 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$2$2.onResponse(KotlinExtensions.kt:53)
at retrofit2.OkHttpCall$1.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)
*Update (My Retrofit)
@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
Any solutions will do. Thanks
thanks for helping me but I was able solve it. Here is the answer that I did
First if you looked in the code where my FishCreateDTO, it has some data attributes. I made sure I followed the sequence of those data attributes requested by my API.
Second I changed the ApiService Interface class in Kotlin
@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
If you noticed I changed from String data type to RequestBody data type. Some say you could still explicitly mentioned the data type like String or Int but to be safer, use RequestBody. Since I've changed my data type in my interface class, this must be reflected into the Repository and ViewModal class. To see the original of these classes, check out my question's content.
Finally, I changed my code in my 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)
)
I ensure that all the data types which are "text/plain" for text/JSON data and for image it is "image/*". Previously, it was in "multipart/form-data" for the image therefore the image couldn't be uploaded thus throwing a 400 Bad Request error.
**Note: Sometimes the api endpoints and the base URL of your API may contain spaces or additional unwanted characters (mentioned by @Ghanshyam) but if it's correct and matching to your API then have a look at your code. The error could be there in your activity or your viewmodel or your repository. It could be also in the Api interface class as well. This answer follows Android MVVM code with Kotlin and ASP.NET Core Web API MVC