Skonfigurowałem aplikację internetową ASP.NET, zaczynając od szablonu MVC 4 / Web API. Wygląda na to, że wszystko działa naprawdę dobrze - żadnych problemów, których jestem świadomy. Użyłem przeglądarki Chrome i Firefox do przeglądania witryny. Przetestowałem za pomocą programu Fiddler i wszystkie odpowiedzi wydają się dotyczyć pieniędzy.
Więc teraz przystępuję do pisania prostego pliku Test.aspx, aby korzystać z tego nowego internetowego interfejsu API. Odpowiednie części scenariusza:
<script type="text/javascript">
$(function () {
$.ajax({
url: "http://mywebapidomain.com/api/user",
type: "GET",
contentType: "json",
success: function (data) {
$.each(data, function (index, item) {
....
});
}
);
},
failure: function (result) {
alert(result.d);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("An error occurred, please try again. " + textStatus);
}
});
});
</script>
To generuje nagłówek REQUEST:
OPTIONS http://host.mywebapidomain.com/api/user HTTP/1.1
Host: host.mywebapidomain.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://mywebapidomain.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: content-type
Connection: keep-alive
W obecnej postaci interfejs API sieci Web zwraca metodę 405 niedozwoloną.
HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Sep 2013 13:28:12 GMT
Content-Length: 96
<Error><Message>The requested resource does not support http method 'OPTIONS'.</Message></Error>
Rozumiem, że zlecenie OPTIONS nie jest domyślnie podłączone do kontrolerów Web API ... Więc umieściłem następujący kod w moim UserController.cs:
// OPTIONS http-verb handler
public HttpResponseMessage OptionsUser()
{
var response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
return response;
}
... i to wyeliminowało błąd 405 Method Not allowed, ale odpowiedź jest całkowicie pusta - żadne dane nie są zwracane:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Sep 2013 12:56:21 GMT
Content-Length: 0
Musi być dodatkowa logika ... Nie wiem, jak poprawnie zakodować metodę Options lub czy kontroler jest nawet odpowiednim miejscem do umieszczenia kodu. Dziwne (dla mnie), że witryna interfejsu API sieci Web reaguje prawidłowo, gdy jest wyświetlana w przeglądarce Firefox lub Chrome, ale wywołanie .ajax powyżej zawiera błędy. Jak obsłużyć sprawdzanie „preflight” w kodzie .ajax? Może powinienem zająć się tym problemem w logice .ajax po stronie klienta? Lub, jeśli jest to problem po stronie serwera z powodu nieobsługiwania czasownika OPTIONS.
Czy ktoś może pomóc? To musi być bardzo częsty problem i przepraszam, jeśli został tutaj rozwiązany. Szukałem, ale nie znalazłem żadnych odpowiedzi, które pomogły.
UPDATE IMHO, jest to problem po stronie klienta i ma związek z powyższym kodem Ajax JQuery. Mówię to, ponieważ Fiddler nie wyświetla żadnych nagłówków błędów 405, gdy uzyskuję dostęp do mywebapidomain / api / user z przeglądarki internetowej. Jedynym miejscem, w którym mogę powielić ten problem, jest wywołanie JQuery .ajax (). Ponadto identyczne wywołanie Ajax powyżej działa dobrze, gdy jest uruchomione na serwerze (w tej samej domenie).
Znalazłem inny post: Prototypowe żądanie AJAX jest wysyłane jako OPCJE, a nie GET; skutkuje błędem 501, który wydaje się być powiązany, ale bezskutecznie majstrowałem przy ich sugestiach. Najwyraźniej JQuery jest zakodowany w taki sposób, że jeśli żądanie Ajax jest międzydomenowe (które jest moim), dodaje kilka nagłówków, które w jakiś sposób wyzwalają nagłówek OPTIONS.
'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,
Po prostu wydaje się, że powinno być dostępne lepsze rozwiązanie niż modyfikacja kodu rdzenia w JQuery ...
Poniższa odpowiedź zakłada, że jest to problem po stronie serwera. Może, tak myślę, ale skłaniam się ku klientowi i dzwonienie do dostawcy hostingu nie pomoże.
źródło
Odpowiedzi:
Jak powiedział Daniel A. White w swoim komentarzu, żądanie OPTIONS jest najprawdopodobniej tworzone przez klienta jako część międzydomenowego żądania JavaScript. Odbywa się to automatycznie przez przeglądarki zgodne ze standardem Cross Origin Resource Sharing (CORS). Wniosek ma charakter wstępny lub przed lotem , wysłanym przed rzeczywistym żądaniem AJAX w celu określenia, które czasowniki i nagłówki żądania są obsługiwane dla CORS. Serwer może zdecydować się na obsługę go dla żadnego, wszystkich lub niektórych czasowników HTTP.
Aby uzupełnić obraz, żądanie AJAX ma dodatkowy nagłówek „Origin”, który wskazuje, skąd pochodzi oryginalna strona, na której znajduje się JavaScript. Serwer może obsługiwać żądania z dowolnego źródła lub tylko dla zestawu znanych, zaufanych źródeł. Zezwolenie na dowolne źródło jest zagrożeniem dla bezpieczeństwa, ponieważ może zwiększyć ryzyko fałszowania żądań między lokacjami (CSRF).
Musisz więc włączyć CORS.
Oto łącze, które wyjaśnia, jak to zrobić w interfejsie ASP.Net Web API
http://www.asp.net/web-api/overview/security/eniring-cross-origin-requests-in-web-api#enable-cors
Opisana tam implementacja pozwala określić między innymi
Ogólnie działa to dobrze, ale musisz mieć świadomość zagrożeń bezpieczeństwa, zwłaszcza jeśli zezwalasz na żądania z różnych źródeł z dowolnej domeny. Pomyśl bardzo uważnie, zanim na to pozwolisz.
Jeśli chodzi o to, które przeglądarki obsługują CORS, Wikipedia twierdzi, że obsługują go następujące silniki:
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing#Browser_support
źródło
Odpowiedź Mike'a Goodwina jest świetna, ale kiedy próbowałem, wydawało się, że jest skierowana do MVC5 / WebApi 2.1. Zależności dla Microsoft.AspNet.WebApi.Cors nie grały dobrze z moim projektem MVC4.
Najprostszy sposób włączenia CORS w WebApi z MVC4 był następujący.
Zauważ, że zezwoliłem na wszystko, sugeruję, abyś ograniczył Origin tylko do klientów, których chcesz, aby Twoje API obsługiwało. Zezwolenie na wszystko jest zagrożeniem dla bezpieczeństwa.
Web.config:
<system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, HEAD" /> <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" /> </customHeaders> </httpProtocol> </system.webServer>
BaseApiController.cs:
Robimy to, aby zezwolić na czasownik http OPTIONS
public class BaseApiController : ApiController { public HttpResponseMessage Options() { return new HttpResponseMessage { StatusCode = HttpStatusCode.OK }; } }
źródło
Po prostu dodaj to do swojej
Application_OnBeginRequest
metody (umożliwi to globalną obsługę CORS dla Twojej aplikacji) i „obsłuż” żądania inspekcji wstępnej:var res = HttpContext.Current.Response; var req = HttpContext.Current.Request; res.AppendHeader("Access-Control-Allow-Origin", req.Headers["Origin"]); res.AppendHeader("Access-Control-Allow-Credentials", "true"); res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name"); res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS"); // ==== Respond to the OPTIONS verb ===== if (req.HttpMethod == "OPTIONS") { res.StatusCode = 200; res.End(); }
* bezpieczeństwo: pamiętaj, że umożliwi to wysyłanie żądań Ajax z dowolnego miejsca na Twój serwer (zamiast tego możesz zezwolić na tworzenie listy źródeł / adresów URL oddzielonych przecinkami, jeśli wolisz).
Użyłem aktualnego pochodzenia klienta zamiast,
*
ponieważ pozwoli to na ustawienie poświadczeń =>Access-Control-Allow-Credentials
na true umożliwi zarządzanie sesjami w różnych przeglądarkachmusisz również włączyć czasowniki usuwania i wstawiania, poprawiania i opcji w swojej
webconfig
sekcjisystem.webServer
, w przeciwnym razie IIS je zablokuje:<handlers> <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" /> <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" /> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" /> <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers>
mam nadzieję że to pomoże
źródło
Application_OnBeginRequest
mi pomogło. Ale jeśli chcesz mieć również możliwość uzyskania danych z autoryzacją, powinieneś również dodaćAuthorization
doAccess-Control-Allow-Headers
Po napotkaniu tego samego problemu w projekcie Web API 2 (i niemożności korzystania ze standardowych pakietów CORS z powodów, których nie warto tutaj omawiać), udało mi się rozwiązać ten problem, wdrażając niestandardowy DelagatingHandler:
public class AllowOptionsHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var response = await base.SendAsync(request, cancellationToken); if (request.Method == HttpMethod.Options && response.StatusCode == HttpStatusCode.MethodNotAllowed) { response = new HttpResponseMessage(HttpStatusCode.OK); } return response; } }
W przypadku konfiguracji interfejsu API sieci Web:
config.MessageHandlers.Add(new AllowOptionsHandler());
Zauważ, że mam również włączone nagłówki CORS w Web.config, podobnie jak niektóre inne odpowiedzi zamieszczone tutaj:
<system.webServer> <modules runAllManagedModulesForAllRequests="true"> <remove name="WebDAVModule" /> </modules> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Headers" value="accept, cache-control, content-type, authorization" /> <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" /> </customHeaders> </httpProtocol> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> </system.webServer>
Zauważ, że mój projekt nie zawiera MVC, tylko Web API 2.
źródło
Udało mi się przezwyciężyć błędy 405 i 404 wyrzucane w żądaniach opcji ajax przed lotem tylko przez niestandardowy kod w global.asax
protected void Application_BeginRequest() { HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); if (HttpContext.Current.Request.HttpMethod == "OPTIONS") { //These headers are handling the "pre-flight" OPTIONS call sent by the browser HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, OPTIONS"); HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept"); HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000"); HttpContext.Current.Response.End(); } }
PS: Weź pod uwagę kwestie bezpieczeństwa, zezwalając na wszystko *.
Musiałem wyłączyć CORS, ponieważ zwracał nagłówek „Access-Control-Allow-Origin” zawierający wiele wartości.
Potrzebne także w web.config:
<handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0"/> <remove name="OPTIONSVerbHandler"/> <remove name="TRACEVerbHandler"/> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/> </handlers>
Aplikacja app.pool musi być ustawiona na tryb zintegrowany.
źródło
Miałem ten sam problem. Dla mnie rozwiązaniem było usunięcie niestandardowego typu zawartości z wywołania jQuery AJAX. Niestandardowe typy treści wyzwalają żądanie przed wyświetleniem. Znalazłem to:
Z tej strony: http://www.asp.net/web-api/overview/security/enables-cross-origin-requests-in-web-api (w sekcji „Żądania inspekcji wstępnej”)
źródło
W interfejsie API sieci Web ASP.NET 2 dodano obsługę CORS. Sprawdź link [ http://www.asp.net/web-api/overview/security/eniring-cross-origin-requests-in-web-api ]
źródło
protected void Application_EndRequest() { if (Context.Response.StatusCode == 405 && Context.Request.HttpMethod == "OPTIONS" ) { Response.Clear(); Response.StatusCode = 200; Response.End(); } }
źródło
Ja też stanąłem przed tym samym problemem.
Wykonaj poniższy krok, aby rozwiązać problem dotyczący zgodności (CORS) w przeglądarkach.
Uwzględnij REDRock w swoim rozwiązaniu z numerem referencyjnym Cors. Dołącz odwołanie do WebActivatorEx do rozwiązania internetowego interfejsu API.
Następnie dodaj plik CorsConfig do folderu App_Start interfejsu Web API.
[assembly: PreApplicationStartMethod(typeof(WebApiNamespace.CorsConfig), "PreStart")] namespace WebApiNamespace { public static class CorsConfig { public static void PreStart() { GlobalConfiguration.Configuration.MessageHandlers.Add(new RedRocket.WebApi.Cors.CorsHandler()); } } }
Po wprowadzeniu tych zmian mogłem uzyskać dostęp do interfejsu webapi we wszystkich przeglądarkach.
źródło
Miałem ten sam problem i tak to naprawiłem:
Po prostu wrzuć to do swojego web.config:
<system.webServer> <modules> <remove name="WebDAVModule" /> </modules> <httpProtocol> <customHeaders> <add name="Access-Control-Expose-Headers " value="WWW-Authenticate"/> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, PATCH, DELETE" /> <add name="Access-Control-Allow-Headers" value="accept, authorization, Content-Type" /> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> <handlers> <remove name="WebDAV" /> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> </system.webServer>
źródło
//In the Application_OnBeginRequest method in GLOBAL.ASX add the following:- var res = HttpContext.Current.Response; var req = HttpContext.Current.Request; res.AppendHeader("Access-Control-Allow-Origin", "*"); res.AppendHeader("Access-Control-Allow-Credentials", "true"); res.AppendHeader("Access-Control-Allow-Headers", "Authorization"); res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS"); // ==== Respond to the OPTIONS verb ===== if (req.HttpMethod == "OPTIONS") { res.StatusCode = 200; res.End(); } //Remove any entries in the custom headers as this will throw an error that there's to //many values in the header. <httpProtocol> <customHeaders> </customHeaders> </httpProtocol>
źródło