Web API 2 无法读取通过 Retrofit 2 发送的 byte[]
Web API 2 cannot read byte[] sent via Retrofit 2
我正在尝试使用 Retrofit 2 和 Web 将对象从我的客户端 sql-lite 数据库上传到 MSSQL API 2.
如果我将 null
或 new byte[1000]
分配给访问,该应用程序可以正常运行。图像,但是每当从 sql-lite 数据库中检索到它的赋值时,我都会收到错误响应代码 400
{
"Message": "The request is invalid.",
"ModelState": {
"visit.Image[0]": [
"An error has occurred."
],
"visit.Image[1]": [
"An error has occurred."
]
}
}
这是我在 android 中的模型:
public class Visit {
public int VisitId;
public String DealerMId;
public byte[] Image; // read image from database (blob data type)
}
这是我如何从数据库中检索值并制作 Visit 对象的代码
public Visit getVisitByVisitId(long visitId) {
SQLiteDatabase db = this.getReadableDatabase();
String selectQuery = "SELECT * FROM " + TABLE_VISIT + " WHERE "
+ KEY_ID + " = " + visitId;
Cursor c = db.rawQuery(selectQuery, null);
if (c != null)
c.moveToFirst();
Visit visit = new Visit();
visit.VisitId = c.getInt(c.getColumnIndex(KEY_ID));
visit.DealerMId = c.getString(c.getColumnIndex(KEY_DEALER_MID));
visit.Image= c.getBlob(c.getColumnIndex(KEY_PICTURE));
return visit ;
}
这是改造服务中使用的接口:
@POST("visits/PostVisit")
public Call<Integer> postVisit(@Body Visit visit);
这是 activity 代码:
Visit vistit = db.getVisitById(1) ;
// Note that : every thing working fine
// if visit.Image = null or visit.Image = new byte[1000] or visit.Image = new byte[]{1,4,3 ..}
// but I am receiving error 400 when visit.Image contains value from database
Call<Integer> call = RetrofitService.postVisit(visit);
call.enqueue(new Callback<Integer>() {
@Override
public void onResponse(Call<Integer> call, Response<Integer>response){
//....
}
@Override
public void onFailure(Call<Integer> call, Throwable t) {
//....
}
});
还有这个网站API 2个代码
[HttpPost]
public IHttpActionResult PostVisit(Visit visit)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Visits.Add(visit);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (VisitExists(visit.VisitId))
{
return Ok(-1);
}
else
{
throw;
}
}
return Ok(visit.VisitId);
}
下面来自 android 工作室的屏幕截图显示了 Visit.Image 的检索内容,我确信它自己的内容没有问题,因为我可以在 [=39] 上阅读它=] ImageView 中的应用程序。
好吧,您正在 posting bytes 从 Java 到 C#。问题是 Java 字节的范围 在 -128 和 127 之间,而 C# 字节的范围是 从 0 到 255。当你 Java 中的 post 字节时,它们会 序列化 为 JSON 字符串(['-128', '-30', '127'] ) 您的 WebApi 控制器接收它们并尝试将它们 反序列化 为 C# 字节(范围从 0 到 255)。不幸的是,由于负数,它失败了。所以你必须让它正确反序列化。
选项 1:在控制器中使用 sbyte
。
在 WebApi 中,将您的模型更改为:
public class Visit
{
public int VisitId;
public String DealerMId;
public sbyte[] Image; // only for C#
}
WebApi contoller 将数组 成功反序列化 为 sbyte
(范围:-128 到 127),然后您可以轻松地将它们转换为字节(取自 SO answer):
byte[] imageBytes = (byte[]) (Array)myVisitModel.Image;
选项 2:从 Java
发送 int[]
将字节作为 int[] 发送,以便发送字节 范围从 0 到 255。
将 Java 中的模型更改为:
public class Visit {
public int VisitId;
public String DealerMId;
public int[] Image;
}
将 byte[] 转换为 int[]:
byte[] imageBytes = c.getBlob(c.getColumnIndex(KEY_PICTURE));
visit.Image= convertToIntArray(imageBytes);
转换方法(taken from Jon Skeet's answer):
public static int[] convertToIntArray(byte[] input)
{
int[] ret = new int[input.length];
for (int i = 0; i < input.length; i++)
{
ret[i] = input[i] & 0xff; // Range 0 to 255, not -128 to 127
}
return ret;
}
注意:我推荐第一个选项,它将在服务器端完成,而第二个选项可能需要long 在转换为 int[]
期间 客户端 .
您可以使用 JsonSerializer(用 Kotlin 编写):
class ByteArrayJsonSerializer : JsonSerializer<ByteArray> {
override fun serialize(src: ByteArray?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
val intArray = JsonArray()
src?.forEach { intArray.add(it.toInt() + 128) }
return intArray
}
}
并添加到您的 Gson:
GsonBuilder()
.registerTypeAdapter(ByteArray::class.java, ByteArrayJsonSerializer()) // to convert java Byte to C# Byte
.create()
我正在尝试使用 Retrofit 2 和 Web 将对象从我的客户端 sql-lite 数据库上传到 MSSQL API 2.
如果我将 null
或 new byte[1000]
分配给访问,该应用程序可以正常运行。图像,但是每当从 sql-lite 数据库中检索到它的赋值时,我都会收到错误响应代码 400
{
"Message": "The request is invalid.",
"ModelState": {
"visit.Image[0]": [
"An error has occurred."
],
"visit.Image[1]": [
"An error has occurred."
]
}
}
这是我在 android 中的模型:
public class Visit {
public int VisitId;
public String DealerMId;
public byte[] Image; // read image from database (blob data type)
}
这是我如何从数据库中检索值并制作 Visit 对象的代码
public Visit getVisitByVisitId(long visitId) {
SQLiteDatabase db = this.getReadableDatabase();
String selectQuery = "SELECT * FROM " + TABLE_VISIT + " WHERE "
+ KEY_ID + " = " + visitId;
Cursor c = db.rawQuery(selectQuery, null);
if (c != null)
c.moveToFirst();
Visit visit = new Visit();
visit.VisitId = c.getInt(c.getColumnIndex(KEY_ID));
visit.DealerMId = c.getString(c.getColumnIndex(KEY_DEALER_MID));
visit.Image= c.getBlob(c.getColumnIndex(KEY_PICTURE));
return visit ;
}
这是改造服务中使用的接口:
@POST("visits/PostVisit")
public Call<Integer> postVisit(@Body Visit visit);
这是 activity 代码:
Visit vistit = db.getVisitById(1) ;
// Note that : every thing working fine
// if visit.Image = null or visit.Image = new byte[1000] or visit.Image = new byte[]{1,4,3 ..}
// but I am receiving error 400 when visit.Image contains value from database
Call<Integer> call = RetrofitService.postVisit(visit);
call.enqueue(new Callback<Integer>() {
@Override
public void onResponse(Call<Integer> call, Response<Integer>response){
//....
}
@Override
public void onFailure(Call<Integer> call, Throwable t) {
//....
}
});
还有这个网站API 2个代码
[HttpPost]
public IHttpActionResult PostVisit(Visit visit)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Visits.Add(visit);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (VisitExists(visit.VisitId))
{
return Ok(-1);
}
else
{
throw;
}
}
return Ok(visit.VisitId);
}
下面来自 android 工作室的屏幕截图显示了 Visit.Image 的检索内容,我确信它自己的内容没有问题,因为我可以在 [=39] 上阅读它=] ImageView 中的应用程序。
好吧,您正在 posting bytes 从 Java 到 C#。问题是 Java 字节的范围 在 -128 和 127 之间,而 C# 字节的范围是 从 0 到 255。当你 Java 中的 post 字节时,它们会 序列化 为 JSON 字符串(['-128', '-30', '127'] ) 您的 WebApi 控制器接收它们并尝试将它们 反序列化 为 C# 字节(范围从 0 到 255)。不幸的是,由于负数,它失败了。所以你必须让它正确反序列化。
选项 1:在控制器中使用 sbyte
。
在 WebApi 中,将您的模型更改为:
public class Visit
{
public int VisitId;
public String DealerMId;
public sbyte[] Image; // only for C#
}
WebApi contoller 将数组 成功反序列化 为 sbyte
(范围:-128 到 127),然后您可以轻松地将它们转换为字节(取自 SO answer):
byte[] imageBytes = (byte[]) (Array)myVisitModel.Image;
选项 2:从 Java
发送int[]
将字节作为 int[] 发送,以便发送字节 范围从 0 到 255。
将 Java 中的模型更改为:
public class Visit {
public int VisitId;
public String DealerMId;
public int[] Image;
}
将 byte[] 转换为 int[]:
byte[] imageBytes = c.getBlob(c.getColumnIndex(KEY_PICTURE));
visit.Image= convertToIntArray(imageBytes);
转换方法(taken from Jon Skeet's answer):
public static int[] convertToIntArray(byte[] input)
{
int[] ret = new int[input.length];
for (int i = 0; i < input.length; i++)
{
ret[i] = input[i] & 0xff; // Range 0 to 255, not -128 to 127
}
return ret;
}
注意:我推荐第一个选项,它将在服务器端完成,而第二个选项可能需要long 在转换为 int[]
期间 客户端 .
您可以使用 JsonSerializer(用 Kotlin 编写):
class ByteArrayJsonSerializer : JsonSerializer<ByteArray> {
override fun serialize(src: ByteArray?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
val intArray = JsonArray()
src?.forEach { intArray.add(it.toInt() + 128) }
return intArray
}
}
并添加到您的 Gson:
GsonBuilder()
.registerTypeAdapter(ByteArray::class.java, ByteArrayJsonSerializer()) // to convert java Byte to C# Byte
.create()