Я очень смущаюсь о новой платформе Microsoft, ASP.NET MVC WebAPI. Я пытаюсь создать полное решение для межсайтового API с данными JSONP.Почему WebAPI не использует форматирование JSONP для десериализации модели?
Во-первых, я изменяю свой WebApiConfig по умолчанию на следующий код.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new {id = RouteParameter.Optional});
// Custom customization
config.Formatters.Clear();
config.Formatters.Add(new JsonpFormatter());
}
}
Я использую jQuery для создания запроса на этот веб-сайт API.
// jQuery will create HTTP GET the following URL
// http://localhost:3557/api/FlightAvailability/SearchFlight?callback=jQuery18206342989655677229_1353568617029&origin=JFK&destination=SLC&isOneWayFlight=false&departFlightDate=Wed%2C+28+Nov+2012+17%3A00%3A00+GMT&returnFlightDate=Wed%2C+05+Dec+2012+17%3A00%3A00+GMT&numberOfGuests=1&numberOfChildren=1&numberOfInfants=1&preferredCurrency=USD&query=%7B+Origin%3A+'JFK'+%7D&flightDate=Wed%2C+28+Nov+2012+17%3A00%3A00+GMT&_=1353568618465
$.ajax
({
url: 'http://localhost:3557/api/FlightAvailability/SearchFlight',
dataType: 'jsonp',
data: $.postify(model),
success: processResponse
});
Я создаю действие для обработки вышеуказанного запроса. Все правильно. Я могу вызвать это действие, но WebAPI не использует форматирование JSONP для десериализации моего объекта запроса.
Однако я пытаюсь напрямую вызвать ContentNegotiator, чтобы получить тот форматтер, который обрабатывает мой запрос. Удивительно, что переговорщик - это мой форматировщик JSONP.
[HttpGet]
public List<FlightInfo> SearchFlight(FlightAvailabilityQuery query)
{
var negotiator = Configuration.Services.GetContentNegotiator();
var negotiatorResult = negotiator.Negotiate(typeof (FlightAvailabilityQuery), Request, Configuration.Formatters);
var flight = new FlightsAvailability();
var result = flight.GetAvailability(WebApiAuthentication.UserInfo.SessionService, query);
return result;
}
Почему WebAPI не использует мой JSONP форматировщик десериализации объекта запроса FlightAvailabilityQuery?
PS. Я пытаюсь сломать всю возможную строку в форматировщике JSONP, но Visual Studio не попадает ни в какую точку прерывания, она напрямую переходит к методу действия без вызова в моем единственном форматировании. Однако, когда я напрямую вызываю ContentNegotiator, он правильно попадает в точку прерывания.
Update # 1 - Добавить JSONP источник форматировщик код
public class JsonpFormatter : JsonMediaTypeFormatter
{
private readonly JsonSerializerSettings _serializerSettings;
private string _jsonpCallbackFunction;
public JsonpFormatter()
{
JsonpParameterName = "callback";
_serializerSettings = new JsonSerializerSettings();
_serializerSettings.TypeNameHandling = TypeNameHandling.Objects;
_serializerSettings.Converters.Add(new IsoDateTimeConverter());
MediaTypeMappings.Add(new ExtendedQueryStringMapping(JsonpParameterName, "application/json"));
}
public string JsonpParameterName { get; set; }
public override bool CanReadType(Type type)
{
return true;
}
public override bool CanWriteType(Type type)
{
return true;
}
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
var formatter = new JsonpFormatter()
{
_jsonpCallbackFunction = GetJsonCallbackFunction(request)
};
// this doesn't work unfortunately
//formatter.SerializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
formatter.SerializerSettings.Converters.Add(new StringEnumConverter());
formatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
return formatter;
}
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
{
// Create a serializer
var serializer = JsonSerializer.Create(_serializerSettings);
// Create task reading the content
return Task.Factory.StartNew(() =>
{
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
{
using (var jsonTextReader = new JsonTextReader(streamReader))
{
return serializer.Deserialize(jsonTextReader, type);
}
}
});
}
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
{
if (string.IsNullOrEmpty(_jsonpCallbackFunction))
return base.WriteToStreamAsync(type, value, stream, content, transportContext);
StreamWriter writer = null;
// write the pre-amble
try
{
writer = new StreamWriter(stream);
writer.Write(_jsonpCallbackFunction + "(");
writer.Flush();
}
catch (Exception ex)
{
try
{
if (writer != null)
writer.Dispose();
}
catch { }
var tcs = new TaskCompletionSource<object>();
tcs.SetException(ex);
return tcs.Task;
}
return base.WriteToStreamAsync(type, value, stream, content, transportContext)
.ContinueWith(innerTask =>
{
if (innerTask.Status == TaskStatus.RanToCompletion)
{
writer.Write(")");
writer.Flush();
}
}, TaskContinuationOptions.ExecuteSynchronously)
.ContinueWith(innerTask =>
{
writer.Dispose();
return innerTask;
}, TaskContinuationOptions.ExecuteSynchronously)
.Unwrap();
}
private string GetJsonCallbackFunction(HttpRequestMessage request)
{
if (request.Method != HttpMethod.Get)
return null;
var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
var queryVal = query[this.JsonpParameterName];
if (string.IsNullOrEmpty(queryVal))
return null;
return queryVal;
}
}
Я думаю, что я мог бы неправильно понять некоторые концепции WebAPI. Я трачу почти 3 дня на работу над этой проблемой, но я никогда не нашел полного решения для JSONP. –
BTW, ContentNegotiator используется только на стороне ответа ...но я удивлен, почему вы видите это поведение, учитывая, что у вас есть только один форматировщик, который является форматировщиком JsonP, и это должно было быть использовано для десериализации запроса ... возможно ли вам поделиться своим кодом приложения? –
Что предлагает метод ReadFromStreamAsync? Вначале я пытаюсь создать некоторый пользовательский класс HttpParameterBinding для десерилизации модели. –