使用asp.net web api v2,我有一个工作的POST方法,我可以从不同的应用程序POST一个自定义类型,并使用JSONConvert我能够反序列化它并在我的POST方法体中使用它。
但是,我的POST参数必须是“object”类型,否则找不到参数(null)。
为什么会这样?我理想情况下将自定义类型作为参数类型,以便我的API文档可以填充正确的请求信息,因为它会根据所使用的参数类型自动生成API文档(看不到覆盖它的方法 - - 如果可能的话会很棒。)
请参阅下面的代码 - 如果“incomingInformation”的类型为“RemoteFileInfo”而不是类型“object”,当我尝试.toString()时会抛出一个null异常。
[Route("api/xx/uploadfiletoalfresco/")]
[HttpPost()]
public ResultStruct UploadFileToAlfresco(object incomingInformation)
{
JObject deserializedJObject = (JObject)JsonConvert.DeserializeObject(incomingInformation.ToString());
SA.Services.RemoteFileInfo convertedRemoteFileInfo = deserializedJObject.ToObject<SA.Services.RemoteFileInfo>();
...
这是我在发送应用程序(vb.net)上的示例代码 - 内容类型设置为application / json并在发送之前被序列化
Dim req As WebRequest = WebRequest.Create(_restEndpointURL & "/uploadfiletoalfresco/")
req.ContentType = "application/json"
req.Method = "POST"
Using sw As New StreamWriter(req.GetRequestStream())
Dim ser As New JavaScriptSerializer
Dim serJSON = ser.Serialize(JsonConvert.SerializeObject(remoteFileInfo))
sw.Write(serJSON)
sw.Flush()
sw.Close()
End Using
下面是我的remoteFileInfo类型,它在接收应用和发送应用上都以这种方式声明。在通过方法JsonConvert.SerializeObject
发送之前,它将转换为JSON字符串Partial Public Class RemoteFileInfo
Public CategoryID As Integer
Public FileName As String
Public Length As Long
Public Note As String
Public SFSubmissionID As String
Public SourceInstance As String
Public Subject As String
Public UserID As Integer
Public Visibility As Boolean
Public riskID As Integer
Public fileByteArray As Byte()
End Class
接收应用定义:
public class RemoteFileInfo
{
public int CategoryID;
public string FileName;
public long Length;
public string Note;
public string SFSubmissionID;
public string SourceInstance;
public string Subject;
public int UserID;
public bool Visibility;
public int riskID;
public Byte[] fileByteArray;
}
来自发送应用程序的示例JSON:
"{"CategoryID":2,"FileName":"Scrum postponed until this afternoon .msg","Length":62976,"Note":"asdf","SFSubmissionID":"006E000000OuYxP","SourceInstance":"Addin","Subject":"Scrum postponed until this afternoon ","UserID":0,"Visibility":true,"riskID":0,"fileByteArray":"VERY LONG STRING"}"
来自fiddler的完整JSON:
POST http://yyyy/api/xxx/uploadfiletoalfresco/ HTTP/1.1
Content-Type: application/json
Host: yyyyy
Content-Length: 84273
Expect: 100-continue
Connection: Keep-Alive
"{\"CategoryID\":2,\"FileName\":\"Scrum postponed until this afternoon .msg\",\"Length\":62976,\"Note\":\"asdf\",\"SFSubmissionID\":\"006E000000OuYxP\",\"SourceInstance\":\"Addin\",\"Subject\":\"Scrum postponed until this afternoon \",\"UserID\":0,\"Visibility\":true,\"riskID\":0,\"fileByteArray\":\"VERY LONG STRING - user edited this is not how it looks in fiddler!\"}"
答案 0 :(得分:0)
此类行为的原因是您的参数必须可通过请求字符串传输。在您的Route
属性中,您说您的方法请求应如下所示:api/xx/uploadfiletoalfresco/
。并且浏览器无法在查询字符串中为对象创建字符串表示形式 - 这只能用于字符串或值类型(如整数)。
所以你有两个选择:让事情保持不变或使用WCF服务,你可以为你的类型提供WSDL。
答案 1 :(得分:0)
我刚刚使用您在帖子中提供的数据对WebAPI 2进行了测试,并使用string
作为fileByteArray
并将模型声明为RemoteFileInfo
对我来说很合适。
public class RemoteFileInfo
{
public int CategoryID;
public string FileName;
public long Length;
public string Note;
public string SFSubmissionID;
public string SourceInstance;
public string Subject;
public int UserID;
public bool Visibility;
public int riskID;
public string fileByteArray;
}
public class ValuesController : ApiController
{
[HttpPost]
public string Test(object incomingInformation)
{
JObject deserializedJObject = (JObject)JsonConvert.DeserializeObject(incomingInformation.ToString());
var convertedRemoteFileInfo = deserializedJObject.ToObject<RemoteFileInfo>();
return convertedRemoteFileInfo.fileByteArray;
}
}
实际上,如果您使用string
,那么Web API模型绑定器将为您完成所有工作:
[HttpPost]
public string Test(RemoteFileInfo incomingInformation)
{
return incomingInformation.fileByteArray; //or whatever
}
但是,如果你真的需要RemoteFileInfo
成为一个数组,你需要从fileByteArray
(这只是一个DTO,因此应该永远不会离开API控制器)转换到另一个类。在处理之前Byte[]
。
您可以编写自定义模型绑定器,但我认为接受更简单的模型并明确转换更简单。
我认为我的答案可能会有些混乱,所以我会尝试澄清我的想法以及我认为在这种情况下会发生什么。
WebAPI模型Binder正在查看POST数据,然后尝试查看它是否适合Action方法中声明的参数类型。如果这是object
,那么它将起作用,因此允许它以object
incomingInformation
实例LI>
您的代码是手动处理序列化的,显然JsonConvert
能够处理将string
转换为Byte[]
在尝试将incomingInformation
声明为类型RemoteFileInfo
时,您看到失败的原因是因为默认的WebAPI模型Binder 无法处理{{1}的转换因此,放弃尝试将传入的POST数据序列化为string->Byte[]
的实例,而是将RemoteFileInfo
传递给方法的null
参数。
当然,任何在incomingInformation
上引用或执行任何操作的尝试都将失败并显示incomingInformation
,依此类推。
让Web API模型绑定器成功绑定该模型的唯一方法是a)将NullReferenceException
更改为字符串(因为它是连线上的字符串),在这种情况下,我的第二个代码示例应该只是工作,或b)写一个自定义模型粘合剂(个人,而不是粉丝)
我当然可能完全错了,但由于你的代码使用Fiddler和我提到的chanes一起工作正常,我怀疑以上几点是情况,或者你还没有包含其他一些因素。< / p>
答案 2 :(得分:0)
感谢所有评论的人。事实证明这个问题与使用JsonConvert.SerializeObject有关。虽然在Fiddler中,json字符串看起来很完美,但我突然想把序列化方法切换到JavaScriptSerializer()。Serialize,RemoteFileInfo对象成功传递和接收(完整的字节数组!)
这是我的请求的最终代码,它将允许接收应用程序将RemoteFileInfo作为参数类型。仍然不确定为什么JsonConvert.Serialize不会起作用; Newstonsoft软件包已安装在客户端和接收应用程序上。
var httpWebRequest = (HttpWebRequest)WebRequest.Create(@"http://yyyy/api/xxx/uploadfiletoalfresco/");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = new JavaScriptSerializer().Serialize(remoteFileInfo);
streamWriter.Write(json);
}
var response = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}