Send complex object to WCF Web API using JSONP
Thanks to Alex Zeitler's article I have my WCF Web API service accepting JSONP requests.
This works really well for simple requests but I have a problem.
One of the functions I need to be able to make accessible is a search function that currently takes a complex object via an http post. Obviously I can't post via JSONP so I'm trying to think of how I can convert this to a get request.
The existing function looks like:
[WebInvoke(UriTemplate = "", Method = "POST")]
public HttpResponseMessage<List<Models.Payload>> FindPayloads(Models.AimiRequest requestValues)
{
// do stuff here
return new HttpResponseMessage<List<searchResult>>(results);
}
The request object that's being passed in is defined as follows:
public class AimiRequest
{
public MetadataQueryParameter[] Metadata { get; set; }
public string ContentType { get; set; }
public string RuleTypes { get; set; }
}
public class MetadataQueryParameter
{
public string Name { get; set; }
public string Value { get; set; }
}
The tricky part is there is an unknown number of metadata parameters and the names and values are not known in advance.
I've tried simply serialising the object to a string and passing that but the service throws a 400 Bad Request (A potentially dangerous Request.Path value was detected from the client (:).)
Anyone out there got any bright ideas?
EDIT: DIRTY HACK ALERT!
Ok, I have a working function but I don't like it. There must be a way of getting this achieved without me having to manually pull the data out of the querystring and unencode it myself.
The client side script (bear in mind this is test code not production)
function findPayload() {
var paramName = $("#ddlMetaType option:selected").val();
var paramValue = $("#txtValue").val();
// build query object
var AimiRequest = new Object();
AimiRequest.ContentType = null;
AimiRequest.RuleTypes = null;
var MetadataQueryParameter = new Object();
MetadataQueryParameter.Name = paramName;
MetadataQueryParameter.Value = paramValue;
AimiRequest.Metadata = new Array();
AimiRequest.Metadata.push(MetadataQueryParameter);
// NB. In production there may be multiple params to push into the array.
// send query to service
$.ajax({
cache: false,
contentType: "application/json",
data: {},
dataType: "jsonp",
error: function (xhr, textStatus, errorThrown) {
switch (xhr.status) {
case 404:
displayNotFound();
break;
default:
alert(xhr.status);
break;
}
},
success: function (response) {
var resultsPane = $("#resultsPane");
$(resultsPane).empty();
$("#payloadTemplate").tmpl(response).appendTo(resultsPane);
},
type: "GET",
url: "http://localhost:63908/search/json?data=" + encodeURIComponent(JSON.stringify(AimiRequest))
});
}
And the server side function that receives it:
[ServiceContra开发者_高级运维ct]
public class SearchResource
{
private IPayloadService _payloadService;
public SearchResource(IPayloadService pService)
{
this._payloadService = pService;
}
[WebGet(UriTemplate = "")]
public HttpResponseMessage<List<Payload>> Search()
{
// find input in querystring
var qString = HttpContext.Current.Request.QueryString["data"];
// Unencode it
var unenc = HttpUtility.UrlDecode(qString);
// deserialise back to the object
var jsSerialiser = new System.Web.Script.Serialization.JavaScriptSerializer();
var myObj = jsSerialiser.Deserialize<AimiRequest>(unenc);
// do search
var metadataParams = new List<KeyValuePair<string, string>>();
foreach (MetadataQueryParameter param in myObj.Metadata)
{
metadataParams.Add(new KeyValuePair<string, string>(param.Name, param.Value));
}
List<Data.Payload> data = _payloadService.FindPayloads(metadataParams, myObj.ContentType, myObj.RuleTypes);
// Map to "viewmodel"
var retVal = AutoMapper.Mapper.Map<List<Data.Payload>, List<Payload>>(data);
// return results
return new HttpResponseMessage<List<Payload>>(retVal);
}
}
1) WCF Web API has the concept of formatters for converting between a HTTP content, accessed via a Stream, and an object. There can be multiple formatters registered and the selection process will take into consideration not only the target object type but also the content's media-type.
2) However, formatters are not used to convert URI and query string parameters. Instead, this process is done by HttpParameterValueConverter(s),which are not extensible.
3) So in conclusion, you have to "unencode" the data yourself. However you can factor out this code out of the operation. Just create an operation handler that receives (input parameter) the query string parameter as a string and returns (output parameter) the strongly typed "unencoded" object (Models.AimiRequest
). The operation parameter's should be of type Models.AimiRequest
.
精彩评论