Как с помощью ajax отправить данные службе RestFul?

Здравствуйте. Я не особо дружу с веб-ом. Поэтому, просьба объяснять по подробней.
Есть служба WCF restful (т.е. принимающая http запросы). Так вот, данные, со службы получаю без проблем (с помощью ajax), а отправить ей не могу, выскакивает ошибка 405, я не знаю, может я не правильный запрос делаю. Через консольку, к стати всё нормально, отправляю данные, принимаю, всё ок.

вот конфиг службы
Код

<configuration>
  <system.web>
    <compilation debug="true" strict="false" explicit="true" targetFramework="4.0" />
    <customErrors mode="Off"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="CORSWebHttpBinding" crossDomainScriptAccessEnabled="True" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
          <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
          <security mode="None">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </webHttpBinding>
    </bindings>
    <services>
      <service name="TZ.Host.TzService" behaviorConfiguration="CORSServiceBehavior">
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="CORSWebHttpBinding" behaviorConfiguration="EndpBehavior"
          contract="TZ.Host.ITzService" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="CORSServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="EndpBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
      </customHeaders>
    </httpProtocol>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>



Global.asax службы (для преодоления кросс браузерности)
Код

protected void Application_BeginRequest(object sender, EventArgs e)
        {
            EnableCrossDomainAjaxCall();
        }

        private void EnableCrossDomainAjaxCall()
        {

            String corsOrigin, corsMethod, corsHeaders;

            corsOrigin = HttpContext.Current.Request.Headers["Origin"];
            corsMethod = HttpContext.Current.Request.Headers["Access-Control-Request-Method"];
            corsHeaders = HttpContext.Current.Request.Headers["Access-Control-Request-Headers"];
            if (corsOrigin == null || corsOrigin == "null")
            {
                corsOrigin = "*";
            }
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", corsOrigin);
            if (corsMethod != null)
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", corsMethod);
            if (corsHeaders != null)
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", corsHeaders);
}



Интерфейс службы
Код

[ServiceContract]
    public interface ITzService
    {
        [OperationContract()]
        [WebInvoke(Method = "GET", UriTemplate = "/GetTestData", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
        List<TZ.Data.Test> GetTestData();


        [WebInvoke(Method = "POST")]
        void AddTestData(TZ.Data.Test value);
    }



Ну и сам класс службы
Код

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class TzService : ITzService
    {
        private static List<TZ.Data.Test> TData = new List<Test>(){new Test(){ID = 0,Name = "a"}, new Test(){ID = 1,Name = "B"}};
        public List<TZ.Data.Test> GetTestData()
        {
            return TData;
        }

        public void AddTestData(TZ.Data.Test value)
        {
            TData.Add(value);
        }
}



Ну и объект данных, которые будут передаваться
Код

[DataContract]
    public class Test
    {
        [DataMember(Name = "id")]
        public int ID { get; set; }
        [DataMember(Name = "name")]
        public string Name { get; set; }
    }



Вот кстати, код консольки - клиента (кому интересно). Тут всё работает отлично
Код

static void Main(string[] args)
        {
            SendData();
            GetData();

            Console.ReadKey();
        }

        private static void SendData()
        {
            string url = @"http://localhost:2337/TzService.svc/AddTestData";

            WebClient client = new WebClient();
            client.Credentials = System.Net.CredentialCache.DefaultCredentials;

            client.Headers["Content-type"] = "application/json";

            TzService.Test employee = new TzService.Test() { id = 11, name = "oooooo11" };

            MemoryStream stream = new MemoryStream();
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TzService.Test));
            serializer.WriteObject(stream, employee);

            client.UploadData(url, "POST", stream.ToArray());
        }

        private static void GetData()
        {
            string url = @"http://localhost:2337/TzService.svc/GetTestData";

            WebClient client = new WebClient();
            client.Credentials = System.Net.CredentialCache.DefaultCredentials;


            byte[] data = client.DownloadData(url);
            Stream stream = new MemoryStream(data);

            DataContractJsonSerializer obj = new DataContractJsonSerializer(typeof(List<TzService.Test>));
            List<TzService.Test> result = obj.ReadObject(stream) as List<TzService.Test>;
            foreach (var p in result)
            {
                Console.WriteLine(p.id + "||" + p.name);
            }
            
        }



А далее, я создал клиент на MVC 3 и делаю запросы, через ajax.
Вот запрос, на получение данных (всё работает отлично)
Код

function Start() {

            $.ajax({
                type: 'GET',
                url: "http://localhost:2337/TzService.svc/GetTestData",
                async: false,
                cache: false,
                dataType: 'json',
                crossDomain: true,

                success: function (data) {
                    ShowData(data);
                },
                error: function (e) {
                    console.log(e.message);
                }
            });
        };



А вот при отправке данных, проблема выскакивает ошибка (я в ajax-е не силён, может я запрос не правильно делаю)
Код

function SendData() {
            var Json = { 'id': 9, 'name': "z" };
            $.ajax({
                url: url,
                data: JSON.stringify(Json),
                type: "POST",
                processData: false,
                async: false,
                contentType: "application/json",
                dataType: "text",
                success:
                    function (response) {
                        
                    },
                error: function (er) {

                }
            });
        }


ошибка

скрин 1
b7a49b1775ca49749579052faae916a2.PNG

скрин 2
a0075da43dfd4b64b84aef315cb39e64.PNG
  • Вопрос задан
  • 2879 просмотров
Решения вопроса 1
@mayorovp
Проблема в том, что вы пытаетесь сделать кросс-доменный запрос, к тому же небезопасный (не являющийся GET или простым POST). Вероятно, это из-за того, что вы указали Content-Type.

Для того, чтобы такой запрос мог быть сделан браузером, сервер должен уметь обрабатывать запрос OPTIONS, отвечая на него корректным заголовком Access-Control-Allow-Headers (ну и Access-Control-Allow-Origin, конечно же).

По запросу "wcf rest options request" в гугле первая же ссылка ведет на вот этовот это. Не самое аккуратное решение (я бы лучше повесил обработку запроса OPTIONS и выдачу заголовков CORS на какой-нибудь атрибут уровня класса) - но и оно сойдет.

PS осторожнее с этим решением - фактически, оно выключает защиту от CORS. Не следует держать приватное API и "обычные" веб-страницы в одном проекте с публичным API.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы