Кроссдоменные запросы в Web API

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 29.10.2015

По умолчанию в целях безопасности браузер ограничивает ajax-запросы между различными доменами. Однако может возникнуть ситуация, когда в Web API нам потребуется выполнять подобные запросы. Для этого нам надо использовать при отправке запроса формат JSONP.

JSONP в Web API

Чтобы задействовать JSONP в Web API, вначале добавим в проект NuGet-пакет WebApiContrib.Formatting.Jsonp:

JSONP in ASP.NET Web API

Затем изменим в проекте Web API файл Global.asax:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using WebApiContrib.Formatting.Jsonp; // добавляем поддержку jsonp

namespace CORSApp
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            GlobalConfiguration.Configuration.AddJsonpFormatter(); // добавляем поддержку jsonp

            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

Допустим, приложение Web API запускается по адресу http://localhost:33384. Для теста создадим второе приложение ASP.NET MVC, которое запускается по другому адресу. Теперь чтобы обратиться из второго приложения к приложению web api через ajax, пропишем в представлении второго приложения следующий код:

<div id="result">

</div>
<div><p><button class="btn" id="request">Запрос</button></p></div>

@section scripts
{
    <script>
        $(function () {

            $("#request").click(function() {

                $.ajax({
                    url: 'http://localhost:33384/api/values/',
                    type: 'GET',
                    dataType: 'jsonp',
                    success: function (data) {

                        $("#result").text(data);
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        $('#result').text(jqXHR.responseText || textStatus);
                    }
                });
            });
        })
    </script>
}

И после нажатия на кнопку будет выполняться запрос к стандартному методу Get контроллера ValuesController приложения Web API.

CORS в Web API

Несколько иное поведение реализует применение стандарта Cross Origin Resource Sharing (CORS), который также позволяет выполнять кроссдоменные запросы, но при этом имеет следующие преимущества: CORS более безопасный, поддерживает не только запросы Get, но и POST, PUT и т.д.

Но чтобы сделать CORS доступным в проекте Web API, надо добавить специальный NuGet-пакет Microsoft.AspNet.WebApi.Cors (пакет добавляется только в проект Web API, к которому идет запрос):

CORS in ASP.NET Web API

Затем откроем файл WebApiConfig.cs в папке App_Start и изменим в нем метод WebApiConfig.Register:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Добавляем поддержку CORS
        config.EnableCors();
		
        config.MapHttpAttributeRoutes();

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

Также в файле конфигурации web.config добавим в узел system.webServer новый элемент modules:

<system.webServer>
<!-- остальное содержимое узла -->
<modules runAllManagedModulesForAllRequests="true"></modules>
</system.webServer>

Затем добавим атрибут [EnableCors] к контроллеру Web API:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors; // пространство имен CORS

namespace CORSApp.Controllers
{
    [EnableCors(origins: "http://localhost:33419", headers: "*", methods: "*")]
    public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // остальные методы
    }
}

Атрибут имеет три параметра:

  • origins: задает набор url, с которых разрешен доступ к данному ресурсу

  • headers: задает набор заголовков, которые допустимы для доступа к ресурсу

  • methods: определяет набор допустимых типов запроса

В использованной версии атрибута предполагается, что доступ к контроллеру values разрешен только с адреса http://localhost:33419 (в конце концевой слеш не используется!), причем разрешен для всех типов запроса - GET, POST, PUT, и любых заголовков.

Из приложения, которое запущено по адресу http://localhost:33419, мы можем обратиться к приложению web api с помощью стандартного запроса ajax:

<div id="result">

</div>
<div><p><button class="btn" id="request" value="Запрос">Запрос</button></p></div>

@section scripts
{
    <script>
        $(function () {

            $("#request").click(function() {

                $.ajax({
                    url: 'http://localhost:33384/api/values/',
                    type: 'GET',
                    //dataType: 'json',
                    success: function (data) {

                        $("#result").text(data);
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        $('#result').text(jqXHR.responseText || textStatus);
                    }
                });
            });
        })
    </script>
}

Если мы хотим, чтобы ресурс был доступен для всех url, то для параметра origins можно поставить звездочку:

[EnableCors(origins: "*", headers: "*", methods: "*")]

Также мы можем задать набор ресурсов, методов, заголовков через запятую:

[EnableCors(origins: "http://localhost:34822,http://www.example.com", headers: "accept,content-type,origin", methods: "get,post")]

Как и другие атрибуты, этот атрибут может быть задан на уровне, контроллера, на уровне одного метода или глобально для всего приложения. Чтобы задать атрибут глобально, изменим класс WebApiConfig в папке App_Start:

using System.Web.Http.Cors;
//........
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute("http://www.example.com", "*", "*");
        config.EnableCors(cors);
		
        config.MapHttpAttributeRoutes();

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

Если мы применяем атрибут глобально или к контроллеру, но при этом хотим исключить из его действия какой-нибудь метод, то для метода можно использовать атрибут [DisableCors]:

[EnableCors(origins: "http://localhost:33419", headers: "*", methods: "*")]
public class ValuesController : ApiController
{
    [DisableCors]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850