1

Microsoft の新しいフレームワークである ASP.NET MVC WebAPI について非常に混乱しています。JSONP データを使用して、クロスサイト API の完全なソリューションを作成しようとしています。

まず、デフォルトの 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 Web サイトへのリクエストを作成します。

// 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 を直接呼び出して、リクエストを処理するフォーマッターを取得しようとしています。negotiatorResult が私の 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 を直接呼び出すと、ブレーク ポイントに正しくヒットしました。

ここに画像の説明を入力

更新 #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;
    }
}
4

1 に答える 1

0

クエリ パラメータをモデル バインドできないため、アクションはヒットしません。またJsonP、HTTP GET のみを対象としているため、フォーマッターは逆シリアル化用に選択されません。FlightAvailabilityQueryがどのように逆シリアル化されると思いますか? あなたのURLから多くのクエリ パラメータを見ました。それをFlightAvailabilityQueryに変換しますか?

これを取得する最も簡単な方法は、FromUri を使用することです。

public List<FlightInfo> SearchFlight([FromUri]FlightAvailabilityQuery query)

なんらかの理由でうまくいかない場合は、origin、isOneWay、destination などの個々のクエリ パラメータ名をアクションに追加してみてください。次に、アクション内で FlightAvailabilityQuery オブジェクトを作成します。また、このモデル バインディング ロジックを再利用したいアクションが多数ある場合は、カスタム パラメーター バインディングを登録して解決できます。これを解決するためにカスタム パラメータ バインディングを登録する方法については、このリンクを参照してください。

お役に立てれば!

于 2013-03-01T06:32:44.070 に答える