programing

ASP를 사용하는 JSONP.NET 웹 API

abcjava 2023. 8. 29. 20:05
반응형

ASP를 사용하는 JSONP.NET 웹 API

저는 ASP에서 새로운 서비스 세트를 만드는 일을 하고 있습니다.웹 API를 사용하는 MVC MVC 4.지금까지는 훌륭합니다.저는 서비스를 만들어 사용하게 되었고, 지금은 JQuery를 사용하여 사용하려고 합니다.저는 Fiddler를 사용하여 JSON 문자열을 돌려받을 수 있고, 괜찮은 것 같지만, 서비스가 별도의 사이트에 존재하기 때문에 "Not Allowed"로 JQuery 오류와 함께 호출하려고 합니다.JSONP를 사용해야 하는 경우가 분명합니다.

웹 API가 새롭다는 것은 알지만, 저는 누군가가 저를 도와줄 수 있기를 바랍니다.

JSONP를 사용하여 웹 API 메서드에 전화를 걸려면 어떻게 해야 합니까?

이 질문을 한 후, 저는 마침내 제가 필요한 것을 발견하여 답변을 드립니다.

JsonpMediaTypeFormatter를 우연히 발견했습니다.에 추가합니다.Application_Start다음을 수행하여 글로벌.ax의.

var config = GlobalConfiguration.Configuration;
config.Formatters.Insert(0, new JsonpMediaTypeFormatter());

그리고 다음과 같은 JQuery AJAX 통화를 진행하는 것이 좋습니다.

$.ajax({
    url: 'http://myurl.com',
    type: 'GET',
    dataType: 'jsonp',
    success: function (data) {
        alert(data.MyProperty);
    }
})

그것은 매우 잘 작동하는 것 같습니다.

다음은 WebAPI RC와 함께 사용할 수 있는 JsonpMediaTypeFormatter의 업데이트된 버전입니다.

public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
{
    private string callbackQueryParameter;

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

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

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

    public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
    {
        string callback;

        if (IsJsonpRequest(out callback))
        {
            return Task.Factory.StartNew(() =>
            {
                var writer = new StreamWriter(stream);
                writer.Write(callback + "(");
                writer.Flush();

                base.WriteToStreamAsync(type, value, stream, content, transportContext).Wait();

                writer.Write(")");
                writer.Flush();
            });
        }
        else
        {
            return base.WriteToStreamAsync(type, value, stream, content, transportContext);
        }
    }


    private bool IsJsonpRequest(out string callback)
    {
        callback = null;

        if (HttpContext.Current.Request.HttpMethod != "GET")
            return false;

        callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];

        return !string.IsNullOrEmpty(callback);
    }
}

다음과 같은 ActionFilter 특성을 사용할 수 있습니다.

public class JsonCallbackAttribute : ActionFilterAttribute
{
    private const string CallbackQueryParameter = "callback";

    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        var callback = string.Empty;

        if (IsJsonp(out callback))
        {
            var jsonBuilder = new StringBuilder(callback);

            jsonBuilder.AppendFormat("({0})", context.Response.Content.ReadAsStringAsync().Result);

            context.Response.Content = new StringContent(jsonBuilder.ToString());
        }

        base.OnActionExecuted(context);
    }

    private bool IsJsonp(out string callback)
    {
        callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];

        return !string.IsNullOrEmpty(callback);
    }
}

그런 다음 액션에 추가합니다.

[JsonCallback]
public IEnumerable<User> User()
{
    return _user;
}

확실히 브라이언의 대답은 정확하지만 이미 Json을 사용하고 있다면 그렇습니다.예쁜 json 날짜와 더 빠른 직렬화를 제공하는 Net 포맷터는 jsonp의 두 번째 포맷터를 추가할 수 없습니다. 두 가지를 결합해야 합니다.Scott Hanselman이 ASP의 출시를 언급했듯이, 어쨌든 그것을 사용하는 것이는 좋은 생각입니다.NET 웹 API는 Json을 사용할 것입니다.기본적으로 Net serializer입니다.

public class JsonNetFormatter : MediaTypeFormatter
    {
        private 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"));
            Encoding = new UTF8Encoding(false, true);

            //we also support jsonp.
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
            MediaTypeMappings.Add(new UriPathExtensionMapping("jsonp", "application/json"));
        }

        public string CallbackQueryParameter
        {
            get { return callbackQueryParameter ?? "jsoncallback"; }
            set { callbackQueryParameter = value; }
        }

        protected override bool CanReadType(Type type)
        {
            if (type == typeof(IKeyValueModel))
                return false;

            return true;
        }

        protected override bool CanWriteType(Type type)
        {
            return true;
        }

        protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders,
            FormatterContext formatterContext)
        {
            // Create a serializer
            JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings);

            // Create task reading the content
            return Task.Factory.StartNew(() =>
            {
                using (StreamReader streamReader = new StreamReader(stream, Encoding))
                {
                    using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader))
                    {
                        return serializer.Deserialize(jsonTextReader, type);
                    }
                }
            });
        }

        protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders,
            FormatterContext formatterContext, TransportContext transportContext)
        {
            string callback;
            var isJsonp = IsJsonpRequest(formatterContext.Response.RequestMessage, out callback);

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

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

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

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

        private bool IsJsonpRequest(HttpRequestMessage request, out string callback)
        {
            callback = null;

            if (request.Method != HttpMethod.Get)
                return false;

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

            return !string.IsNullOrEmpty(callback);
        }
    }

Rick Strahl의 구현은 RC를 사용하는 저에게 가장 효과적이었습니다.

JSONP는 Http GET 요청에서만 작동합니다.asp.net 웹 API에는 모든 http 동사와 잘 작동하는 CORS 지원이 있습니다.

기사는 당신에게 도움이 될 수 있습니다.

업데이트됨

public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
    {
        private string callbackQueryParameter;

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

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

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

        public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, 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();
                });
            }
            else
            {
                return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
            }
        }

        private bool IsJsonpRequest(out string callback)
        {
            callback = null;

            if (HttpContext.Current.Request.HttpMethod != "GET")
                return false;

            callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];

            return !string.IsNullOrEmpty(callback);
        }
    }

다음은 RTM 버전의 웹 API와 함께 작동하는 몇 가지 개선된 업데이트 버전입니다.

  • 의 인코딩을 기준으로 인코딩을 합니다.Accept-Encoding머리글. 머리글.new StreamWriter()UTF-8을 합니다.:base.WriteToStreamAsync다른 인코딩을 사용하여 출력이 손상될 수 있습니다.
  • 을 JSONP 파일에 .application/javascript Content-Type하지만, ""; "JSONP"를 합니다.application/json표제의이 작업은 중첩된 상태에서 수행됩니다.Mappingclass (cf.JSONP를 제공하기 위한 최상의 콘텐츠 유형?)
  • 설및 플싱오를포드기다니합헤버건러▁▁▁const의 시공 및 오버헤드를 합니다.StreamWriter직접 바이트를 가져와 출력 스트림에 씁니다.
  • 의 "Task Parallel Library"를 합니다.ContinueWith여러 작업을 연결하는 메커니즘입니다.

코드:

public class JsonpMediaTypeFormatter : JsonMediaTypeFormatter
{
  private string _callbackQueryParameter;

  public JsonpMediaTypeFormatter()
  {
    SupportedMediaTypes.Add(DefaultMediaType);
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/javascript"));

    // need a lambda here so that it'll always get the 'live' value of CallbackQueryParameter.
    MediaTypeMappings.Add(new Mapping(() => CallbackQueryParameter, "application/javascript"));
  }

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

  public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content,
                                          TransportContext transportContext)
  {
    var callback = GetCallbackName();

    if (!String.IsNullOrEmpty(callback))
    {
      // select the correct encoding to use.
      Encoding encoding = SelectCharacterEncoding(content.Headers);

      // write the callback and opening paren.
      return Task.Factory.StartNew(() =>
        {
          var bytes = encoding.GetBytes(callback + "(");
          writeStream.Write(bytes, 0, bytes.Length);
        })
      // then we do the actual JSON serialization...
      .ContinueWith(t => base.WriteToStreamAsync(type, value, writeStream, content, transportContext))

      // finally, we close the parens.
      .ContinueWith(t =>
        {
          var bytes = encoding.GetBytes(")");
          writeStream.Write(bytes, 0, bytes.Length);
        });
    }
    return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
  }

  private string GetCallbackName()
  {
    if (HttpContext.Current.Request.HttpMethod != "GET")
      return null;
    return HttpContext.Current.Request.QueryString[CallbackQueryParameter];
  }

  #region Nested type: Mapping

  private class Mapping : MediaTypeMapping
  {
    private readonly Func<string> _param; 

    public Mapping(Func<string> discriminator, string mediaType)
      : base(mediaType)
    {
      _param = discriminator;
    }

    public override double TryMatchMediaType(HttpRequestMessage request)
    {
      if (request.RequestUri.Query.Contains(_param() + "="))
        return 1.0;
      return 0.0;
    }
  }

  #endregion
}

저그해의 "킹을들 "고는"의 .Func<string>생성자의 변수이지만,클래스만 에, 클스생매변만지이수의,▁the▁▁it▁in다▁problem를 볼 수 없기 때문에 를 해결하는 이었습니다. C#은 정적 내부 클래스만 가지고 있기 때문에, 그것은 볼 수 없습니다.CallbackQueryParameter소물유. 달전을 하는 것.Func람다에서 속성을 바인딩합니다.Mapping에 에액세수있에서 수 .TryMatchMediaType좀 더 우아한 방법이 있다면 댓글을 달아주세요!

유감스럽게도 저는 평이 부족해서 답변을 올리겠습니다.@Justin은 WebApiContrib 실행에 대한 문제를 제기했습니다.포맷.표준 Json 포맷터와 함께 Json 포맷터.이 문제는 최신 릴리스(실제로는 얼마 전에 릴리스됨)에서 해결되었습니다.또한 최신 웹 API 릴리스에서도 작동해야 합니다.

조퍼를, 토마스.위의 Peter Moberg가 제시한 답변은 그가 상속받은 JsonMediaTypeFormatter가 이미 NewtonSoft Json serializer를 사용하므로 RC 버전에 대해 정확해야 하며, 따라서 그가 가지고 있는 것은 변경 없이 작동해야 합니다.

하지만, 당신이 다음을 할 수 있을 때, 도대체 왜 사람들은 여전히 매개변수를 사용하고 있습니까?

public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext transportContext)
        {
            var isJsonpRequest = IsJsonpRequest();

            if(isJsonpRequest.Item1)
            {
                return Task.Factory.StartNew(() =>
                {
                    var writer = new StreamWriter(stream);
                    writer.Write(isJsonpRequest.Item2 + "(");
                    writer.Flush();
                    base.WriteToStreamAsync(type, value, stream, contentHeaders, transportContext).Wait();
                    writer.Write(")");
                    writer.Flush();
                });
            }

            return base.WriteToStreamAsync(type, value, stream, contentHeaders, transportContext);
        }

        private Tuple<bool, string> IsJsonpRequest()
        {
            if(HttpContext.Current.Request.HttpMethod != "GET")
                return new Tuple<bool, string>(false, null);

            var callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];

            return new Tuple<bool, string>(!string.IsNullOrEmpty(callback), callback);
        }

자체 JSONP 포맷터 버전을 호스팅하는 대신 WebApiContrib을 설치할 수 있습니다.포맷.이미 구현된 Jsonp NuGet 패키지(에 적합한 버전을 선택하십시오).NET Framework).

를 다에이포추가에 합니다.Application_Start:

GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonpMediaTypeFormatter(new JsonMediaTypeFormatter()));

HttpSelfHostServer를 사용하는 사용자의 경우 이 코드 섹션은 HttpContext에서 실패합니다.자체 호스트 서버에 없기 때문에 현재입니다.

private Tuple<bool, string> IsJsonpRequest()
{
if(HttpContext.Current.Request.HttpMethod != "GET")
 return new Tuple<bool, string>(false, null);
 var callback = HttpContext.Current.Request.QueryString[CallbackQueryParameter];
 return new Tuple<bool, string>(!string.IsNullOrEmpty(callback), callback);
 }

그러나 이 재정의를 통해 자체 호스트 "컨텍스트"를 가로챌 수 있습니다.

public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
        {
            _method = request.Method;
            _callbackMethodName =
                request.GetQueryNameValuePairs()
                       .Where(x => x.Key == CallbackQueryParameter)
                       .Select(x => x.Value)
                       .FirstOrDefault();

            return base.GetPerRequestFormatterInstance(type, request, mediaType);
        }

요청 사항.메소드는 "GET", "POST" 등을 제공하며 GetQueryNameValuePairs는 ?callback 매개 변수를 검색할 수 있습니다.따라서 수정된 코드는 다음과 같습니다.

private Tuple<bool, string> IsJsonpRequest()
 {
     if (_method.Method != "GET")
     return new Tuple<bool, string>(false, null);

     return new Tuple<bool, string>(!string.IsNullOrEmpty(_callbackMethodName), _callbackMethodName);
}

이것이 여러분 중 일부에게 도움이 되기를 바랍니다.이렇게 하면 HttpContext 심이 필요하지 않습니다.

c.

이것 좀 보세요.도움이 되는지 보세요.

웹 API를 사용한 JSONP

컨텍스트가 다음과 같은 경우Web Api감사하고 참고하는010227leo의 대답, 당신은 고려해야 합니다.WebContext.Current앞으로 있을 것null.

그래서 나는 그의 코드를 다음과 같이 업데이트했습니다.

public class JsonCallbackAttribute
    : ActionFilterAttribute
{
    private const string CallbackQueryParameter = "callback";

    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        var callback = context.Request.GetQueryNameValuePairs().Where(item => item.Key == CallbackQueryParameter).Select(item => item.Value).SingleOrDefault();

        if (!string.IsNullOrEmpty(callback))
        {
            var jsonBuilder = new StringBuilder(callback);

            jsonBuilder.AppendFormat("({0})", context.Response.Content.ReadAsStringAsync().Result);

            context.Response.Content = new StringContent(jsonBuilder.ToString());
        }

        base.OnActionExecuted(context);
    }
}

우리는 두 가지 방법으로 CORS(Cross-origin 자원 공유) 문제를 해결할 수 있습니다.

Jsonp 사용 2) Cors 활성화

Jsonp-를 사용하여 Jsonp를 사용하려면 WebApiContrib을 설치해야 합니다.포맷.Jsonp nuget 패키지 및 WebApiConfig.cs 에 JsonpFormmater를 추가해야 합니다.enter image description here 스크린샷을 참조하십시오.

제이쿼리 코드

코르스 활성화

마이크로소프트를 추가해야 합니다.AsNet.WebApi.코르스 너겟 패키지 및 WebApiConfig.cs 에서 코르스를 활성화해야 함 스크린샷 참조.

enter image description here

더 많은 참고를 위해, 당신은 아래 링크를 사용하여 GitHub에 대한 나의 샘플 보고서를 참조할 수 있습니다.https://github.com/mahesh353/Ninject.WebAPi/tree/develop

언급URL : https://stackoverflow.com/questions/9421312/jsonp-with-asp-net-web-api

반응형