当前位置: 动力学知识库 > 问答 > 编程问答 >

.net - jsonp media type formatter c# issue

问题描述:

Probably a dumb question, but I'm trying to add jsonp support to my webapi app. I added this line to my webapiconfig.cs, but it's failing because 2 arguments are expected for the jsonpmediatypeformatter constructor:

 public static void Register(HttpConfiguration configuration)

{

configuration.Routes.MapHttpRoute("API Default", "api/v1/{controller}/{id}",

new { id = RouteParameter.Optional });

var appXmlType = configuration.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");

configuration.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);

**configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter();**

}

The first of which seems to be of type mediatypeformatter, which doesn't make much sense to me. I tried:

configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter(new JsonMediaTypeFormatter(),"jsonp"));

which does correctly wrap the response in a function called jsonp, but also breaks standard json responses.

Any ideas?

网友答案:

It took me long as well to get it working. This code is based on https://gist.github.com/ninnemana/3715076 - which did not work for me because HttpContext.Current returns null with the 4.5 version of the .NET framework.

This is the class:

public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
    {

        private string _callbackQueryParamter;
        private HttpRequestMessage HttpRequest;

        public JsonpMediaTypeFormatter()
        {
            SupportedMediaTypes.Add(DefaultMediaType);
            SupportedMediaTypes.Add(new MediaTypeWithQualityHeaderValue("text/javascript"));

            MediaTypeMappings.Add(new UriPathExtensionMapping("jsonp", DefaultMediaType));
        }


        public string CallbackQueryParameter
        {
            get { return _callbackQueryParamter ?? "callback"; }
            set { _callbackQueryParamter = value; }
        }

        public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type,
                                            HttpRequestMessage request,  MediaTypeHeaderValue mediaType)
        {
            HttpRequest = request;
            return base.GetPerRequestFormatterInstance(type, request, mediaType);
        }

        public override System.Threading.Tasks.Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
        {
            string callback;

            if (IsJsonpRequest(out callback))
            {
                return Task.Factory.StartNew(() =>
                {
                    var writer = new StreamWriter(writeStream);
                    writer.Write(callback + "(");
                    writer.Flush();
                    base.WriteToStreamAsync(type, value, writeStream, content, transportContext).Wait();
                    writer.Write(")");
                    writer.Flush();
                });
            }
            return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
        }

        private bool IsJsonpRequest(out string callback)
        {
            var query = HttpUtility.ParseQueryString(HttpRequest.RequestUri.Query);
            callback = query[CallbackQueryParameter];

            return !string.IsNullOrEmpty(callback);
        }
    }

And this needs to be added to your startup:

public class Startup
    {
        // This code configures Web API. The Startup class is specified as a type
        // parameter in the WebApp.Start method.
        public void Configuration(IAppBuilder appBuilder)
        {
            // Configure Web API for self-host. 
            HttpConfiguration config = new HttpConfiguration();

            // Remove the XML formatter (only want JSON) see http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization
            config.Formatters.Remove(config.Formatters.XmlFormatter);
            // add jsonp formatter as the one with the highest prio
            config.Formatters.Insert(0, new JsonpMediaTypeFormatter());

            // routes

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{arg1}/{arg2}",
                defaults: new { arg1 = RouteParameter.Optional, arg2 = RouteParameter.Optional }
            );

            appBuilder.UseWebApi(config);
        }
    }
网友答案:

So, I've read a ton of these about adding JSONP support to WebAPI. I've tried about all of them.

Then, I took the time to read the regular Json formatter I've been using all along....hey, it supports Jsonp as well.

Here is the class:

public class JsonNetFormatter : MediaTypeFormatter
{
    private readonly JsonSerializerSettings _jsonSerializerSettings;
    private string _callbackQueryParameter;

    public JsonNetFormatter(JsonSerializerSettings jsonSerializerSettings)
    {
        _jsonSerializerSettings = jsonSerializerSettings ?? new JsonSerializerSettings();

        // Fill out the mediatype and encoding we support
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
        SupportedEncodings.Add(new UTF8Encoding(false, true));

        //we also support jsonp.
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
    }

    private Encoding Encoding
    {
        get { return SupportedEncodings[0]; }
    }

    public string CallbackQueryParameter
    {
        get { return _callbackQueryParameter ?? "callback"; }
        set { _callbackQueryParameter = value; }
    }

    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 JsonNetFormatter(_jsonSerializerSettings)
        {
            JsonpCallbackFunction = GetJsonCallbackFunction(request)
        };

        return formatter;
    }

    private string GetJsonCallbackFunction(HttpRequestMessage request)
    {
        if (request.Method != HttpMethod.Get)
            return null;

        var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
        var queryVal = query[CallbackQueryParameter];

        if (string.IsNullOrEmpty(queryVal))
            return null;

        return queryVal;
    }

    private string JsonpCallbackFunction { get; set; }

    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
    {
        // Create a serializer
        JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings);

        // Create task reading the content
        return Task.Factory.StartNew(() =>
        {
            using (var streamReader = new StreamReader(readStream, SupportedEncodings[0]))
            {
                using (var jsonTextReader = new JsonTextReader(streamReader))
                {
                    return serializer.Deserialize(jsonTextReader, type);
                }
            }
        });
    }

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        var isJsonp = JsonpCallbackFunction != null;

        // Create a serializer
        JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings);

        // Create task writing the serialized content
        return Task.Factory.StartNew(() =>
        {
            using (var jsonTextWriter = new JsonTextWriter(new StreamWriter(writeStream, Encoding)) { CloseOutput = false })
            {
                if (isJsonp)
                {
                    jsonTextWriter.WriteRaw(JsonpCallbackFunction + "(");
                    jsonTextWriter.Flush();
                }

                serializer.Serialize(jsonTextWriter, value);
                jsonTextWriter.Flush();

                if (isJsonp)
                {
                    jsonTextWriter.WriteRaw(")");
                    jsonTextWriter.Flush();
                }
            }
        });
    }
}

Then, in your global.asax.cs add this little beauty:

private static void AddJsonFormatterAndSetDefault()
    {
        var serializerSettings = new JsonSerializerSettings();
        serializerSettings.Converters.Add(new IsoDateTimeConverter());
        var jsonFormatter = new JsonNetFormatter(serializerSettings);
        jsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
        GlobalConfiguration.Configuration.Formatters.Insert(0, jsonFormatter);
    }

And call it from Application_Start

This works for me to support both json and jsonp.

分享给朋友:
您可能感兴趣的文章:
随机阅读: