2011-08-26 66 views
0

感谢Alex Zeitler's article我让我的WCF Web API服务接受JSONP请求。使用JSONP将复杂对象发送到WCF Web API

这对于简单的请求非常有效,但我遇到了问题。

我需要能够访问的函数之一是一个搜索函数,它通过http post发送一个复杂的对象。很明显,我不能通过JSONP发布,所以我想着如何将其转换为get请求。

现有函数看起来像:

[WebInvoke(UriTemplate = "", Method = "POST")] 
public HttpResponseMessage<List<Models.Payload>> FindPayloads(Models.AimiRequest requestValues) 
{ 
    // do stuff here 
    return new HttpResponseMessage<List<searchResult>>(results); 
} 

其在被定义为被传递的请求对象如下:

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; } 
} 

棘手的部分是有元数据参数和未知数量名称和价值未提前知道。

我试着简单地连载的对象为字符串,并通过这一点,但该服务将抛出一个400错误请求(从客户端(:)检测到有潜在危险的Request的值。)

任何人在有什么好的想法?

编辑:脏黑客警报!

好的,我有一个工作功能,但我不喜欢它。必须有一种方法可以实现这一点,而不必手动将数据从查询字符串中提取出来并自行解码。

的客户端脚本(记住,这是测试代码不生产)

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)) 
    }); 

} 

和接收它的服务器端功能:

[ServiceContract] 
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); 
    } 
} 
+0

你如何序列化AimiRequest对象(我假设发出GET的客户端将是一个普通的浏览器)? XML中的XML将被ASP.NET管道拒绝(如果在ASP.NET上托管而不是自托管)。 –

+0

我在使用JSON2插件中的JSON.stringify()函数进行序列化。是的,进行GET的客户端是浏览器中的jquery ajax调用。 – Nick

回答

2

1)WCF的Web API具有概念用于在通过Stream访问的HTTP内容和对象之间进行转换的格式化程序。可以注册多个格式化程序,并且选择过程不仅考虑目标对象类型,还考虑内容的媒体类型。

2)但是,格式化程序不用于转换URI和查询字符串参数。相反,这个过程由HttpParameterValueConverter(s)完成,这是不可扩展的。

3)所以最后,你必须“自己解密”数据。但是,您可以将这些代码排除在操作之外。只需创建一个操作处理程序,它接收(输入参数)查询字符串参数作为字符串,并返回(输出参数)强类型的“未编码”对象(Models.AimiRequest)。操作参数的类型应该是Models.AimiRequest

+0

我写了一篇关于自定义操作参数转换的文章:http://pfelix.wordpress.com/2011/09/24/wcf-web-apicustom-parameter-conversion/ –