Отправка сложного объекта в WCF Web API с использованием JSONP - PullRequest
0 голосов
/ 26 августа 2011

Благодаря статье Алекса Цайтлера У меня есть служба веб-API WCF, принимающая запросы JSONP.

Это очень хорошо работает для простых запросов, но у меня есть проблема.

Одной из функций, которые мне нужно сделать доступными, является функция поиска, которая в настоящее время принимает сложный объект через сообщение http.Очевидно, что я не могу публиковать сообщения через 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.Path было обнаружено клиентом (:).)

Любой, кто получил какие-либо яркие идеи?

РЕДАКТИРОВАТЬ: DIRTY HACK ALERT!

Хорошо, у меня есть рабочая функция, но она мне не нравится.Должен быть способ добиться этого без необходимости вручную извлекать данные из строки запроса и самостоятельно дешифровать их.

Сценарий на стороне клиента (имейте в виду, что это тестовый код, а не рабочий)

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

1 Ответ

2 голосов
/ 01 сентября 2011

1) WCF Web API имеет концепцию форматеров для преобразования между содержимым HTTP, доступ к которому осуществляется через поток, и объектом. Может быть зарегистрировано несколько форматеров, и процесс выбора будет учитывать не только тип целевого объекта, но и медиа-тип содержимого.

2) Однако средства форматирования не используются для преобразования параметров URI и строки запроса. Вместо этого этот процесс выполняется HttpParameterValueConverter (s), которые не являются расширяемыми.

3) Итак, в заключение вы должны самостоятельно «расшифровать» данные. Однако вы можете исключить этот код из операции. Просто создайте обработчик операции, который получает (входной параметр) параметр строки запроса в виде строки и возвращает (выходной параметр) строго типизированный «незакодированный» объект (Models.AimiRequest). Параметры операции должны иметь тип Models.AimiRequest.

...