Próbuję użyć funkcji pobierania i przekazywania w trybie: no-cors

167

Mogę trafić na ten punkt końcowy http://catfacts-api.appspot.com/api/facts?number=99przez Postman i wracaJSON

Dodatkowo używam aplikacji create-react-app i chciałbym uniknąć konfigurowania jakiejkolwiek konfiguracji serwera.

W moim kodzie klienta próbuję fetchzrobić to samo, ale pojawia się błąd:

Żądany zasób nie zawiera nagłówka „Access-Control-Allow-Origin”. Dlatego źródło „ http: // localhost: 3000 ” nie ma dostępu. Jeśli nieprzezroczysta odpowiedź spełnia Twoje potrzeby, ustaw tryb żądania na „brak elementów”, aby pobrać zasób z wyłączonym mechanizmem CORS.

Więc próbuję przekazać obiekt do mojego Fetch, który wyłączy CORS, na przykład:

fetch('http://catfacts-api.appspot.com/api/facts?number=99', { mode: 'no-cors'})
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });

Co ciekawe, błąd, który otrzymuję, jest w rzeczywistości błędem składniowym z tą funkcją. Nie jestem pewien, czy mój rzeczywisty fetchjest uszkodzony, ponieważ kiedy usuwam obiekt {mode: 'no-cors'} i dostarczam go z innym adresem URL, działa dobrze.

Próbowałem również przekazać obiekt { mode: 'opaque'}, ale to zwraca pierwotny błąd z góry.

Wierzę, że wszystko, co muszę zrobić, to wyłączyć CORS. Czego mi brakuje?

dwww
źródło

Odpowiedzi:

292

mode: 'no-cors'nie sprawi, że wszystko zacznie działać. W rzeczywistości pogarsza to sytuację, ponieważ jednym z efektów jest mówienie przeglądarkom: „Zablokuj mojemu kodowi JavaScript frontendu przeglądanie zawartości treści odpowiedzi i nagłówków w każdych okolicznościach”. Oczywiście, że prawie nigdy tego nie chcesz.

To, co dzieje się w przypadku żądań między źródłami z frontendowego JavaScript, polega na tym, że przeglądarki domyślnie blokują kod frontendu przed dostępem do zasobów między źródłami. Jeśli Access-Control-Allow-Originjest w odpowiedzi, przeglądarki złagodzą to blokowanie i pozwolą Twojemu kodowi uzyskać dostęp do odpowiedzi.

Ale jeśli witryna wysyła „nie” Access-Control-Allow-Originw swoich odpowiedziach, kod frontendu nie może uzyskać bezpośredniego dostępu do odpowiedzi z tej witryny. W szczególności nie można tego naprawić, określając mode: 'no-cors'(w rzeczywistości zapewni to, że kod frontendu nie będzie miał dostępu do treści odpowiedzi).

Jednak jedna rzecz, która będzie działać: jeśli wysyłasz żądania przez pełnomocnika Cors , jak poniżej:

var proxyUrl = 'https://cors-anywhere.herokuapp.com/',
    targetUrl = 'http://catfacts-api.appspot.com/api/facts?number=99'
fetch(proxyUrl + targetUrl)
  .then(blob => blob.json())
  .then(data => {
    console.table(data);
    document.querySelector("pre").innerHTML = JSON.stringify(data, null, 2);
    return data;
  })
  .catch(e => {
    console.log(e);
    return e;
  });
<pre></pre>

Uwaga: jeśli spróbujesz użyć https://cors-anywhere.herokuapp.com, okaże się, że jest wyłączony , możesz również łatwo wdrożyć własne proxy na Heroku w dosłownie zaledwie 2-3 minuty, za pomocą 5 poleceń:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

Po uruchomieniu tych poleceń otrzymasz własny serwer CORS Anywhere działający np . Pod adresem https://cryptic-headland-94862.herokuapp.com/ . Więc zamiast poprzedzać adres URL żądania https://cors-anywhere.herokuapp.comprzedrostkiem, należy poprzedzić go adresem URL własnej instancji; np . https://cryptic-headland-94862.herokuapp.com/https://example.com .


Mogę trafić na ten punkt końcowy http://catfacts-api.appspot.com/api/facts?number=99przez Postman

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS wyjaśnia, dlaczego jest tak, że chociaż możesz uzyskać dostęp do odpowiedzi za pomocą programu Postman, przeglądarki nie pozwalają na dostęp do odpowiedzi z różnych źródeł z frontendu Kod JavaScript działający w aplikacji internetowej, chyba że odpowiedź zawiera Access-Control-Allow-Originnagłówek odpowiedzi.

http://catfacts-api.appspot.com/api/facts?number=99 nie ma Access-Control-Allow-Originnagłówka odpowiedzi, więc nie ma możliwości, aby kod frontendu mógł uzyskać dostęp do odpowiedzi z różnych źródeł.

Twoja przeglądarka może uzyskać odpowiedź w porządku i możesz ją zobaczyć w programie Postman, a nawet w narzędziach programistycznych przeglądarki - ale to nie znaczy, że przeglądarki udostępnią ją Twojemu kodowi. Nie zrobią tego, ponieważ nie ma Access-Control-Allow-Originnagłówka odpowiedzi. Więc zamiast tego musisz użyć proxy, aby go uzyskać.

Serwer proxy wysyła żądanie do tej witryny, pobiera odpowiedź, dodaje Access-Control-Allow-Originnagłówek odpowiedzi i wszelkie inne potrzebne nagłówki CORS, a następnie przekazuje je z powrotem do kodu żądającego. A ta odpowiedź z Access-Control-Allow-Origindodanym nagłówkiem jest tym, co widzi przeglądarka, więc przeglądarka pozwala kodowi frontendu faktycznie uzyskać dostęp do odpowiedzi.


Więc próbuję przekazać obiekt do mojego Fetch, który wyłączy CORS

Nie chcesz tego robić. Aby było jasne, kiedy mówisz, że chcesz „wyłączyć CORS”, wydaje się, że w rzeczywistości chcesz wyłączyć politykę tego samego pochodzenia . Sam CORS jest w rzeczywistości sposobem na to - CORS jest sposobem na poluzowanie zasad tego samego pochodzenia, a nie sposobem na ich ograniczenie.

W każdym razie to prawda, że ​​możesz - tylko w swoim środowisku lokalnym - robić takie rzeczy, jak nadawanie flagom czasu wykonywania przeglądarki, aby wyłączyć zabezpieczenia i działać niezabezpiecznie, lub możesz zainstalować rozszerzenie przeglądarki lokalnie, aby obejść zasady tego samego pochodzenia, ale wszystko to robi to lokalna zmiana sytuacji tylko dla Ciebie.

Bez względu na to, co zmienisz lokalnie, każdy inny użytkownik, który spróbuje użyć Twojej aplikacji, nadal będzie miał do czynienia z polityką tego samego pochodzenia i nie ma możliwości wyłączenia tego dla innych użytkowników Twojej aplikacji.

Najprawdopodobniej nigdy nie chcesz używać mode: 'no-cors'w praktyce, z wyjątkiem kilku ograniczonych przypadków , a nawet wtedy, gdy wiesz dokładnie, co robisz i jakie są efekty. Dzieje się tak, ponieważ ustawienie mode: 'no-cors'faktycznie mówi do przeglądarki: „Zablokuj mojemu kodowi JavaScript frontendu wgląd w treść odpowiedzi i nagłówki w każdych okolicznościach”. W większości przypadków to oczywiście nie jest to, czego chcesz.


Jeśli chodzi o przypadki, w których chciałbyś rozważyć użycie mode: 'no-cors', zapoznaj się z odpowiedzią w artykule Jakie ograniczenia dotyczą nieprzejrzystych odpowiedzi? po szczegóły. Istota sprawy polega na tym, że przypadki są:

  • W przypadku ograniczonej gdy jesteś przy użyciu JavaScript, aby umieścić zawartość z innego pochodzenia do <script>, <link rel=stylesheet>, <img>, <video>, <audio>, <object>, <embed>, lub <iframe>elementu (który działa z powodu osadzania zasobów krzyżowo-origin jest dozwolona dla osób) - ale z jakiegoś powodu don” Nie chcesz lub nie możesz tego zrobić, używając tylko znaczników dokumentu jako adresu URL zasobu jako atrybutu hreflub srcdla elementu.

  • Gdy jedyną rzeczą, którą chcesz zrobić z zasobem, jest buforowanie go. Jak wspomniano w odpowiedzi, jakie ograniczenia dotyczą nieprzejrzystych odpowiedzi? , w praktyce scenariusz ma zastosowanie, gdy używasz Service Workers, w którym to przypadku interfejs API, który ma znaczenie, to interfejs API pamięci podręcznej .

Ale nawet w tych ograniczonych przypadkach istnieje kilka ważnych problemów, o których należy pamiętać; zobacz odpowiedź w sekcji Jakie ograniczenia dotyczą nieprzejrzystych odpowiedzi? po szczegóły.


Próbowałem też przejść do obiektu { mode: 'opaque'}

Nie ma mode: 'opaque'trybu żądania - opaquezamiast tego jest po prostu właściwością odpowiedzi , a przeglądarki ustawiają tę nieprzezroczystą właściwość w odpowiedziach z żądań wysyłanych za pomocą tego no-corstrybu.

Ale nawiasem mówiąc, słowo nieprzezroczysty jest dość wyraźnym sygnałem o naturze odpowiedzi, którą otrzymujesz: „nieprzejrzysty” oznacza, że ​​nie możesz jej zobaczyć.

Sidehowbarker
źródło
4
Uwielbiam cors-anywhereobejście prostych przypadków użycia innych niż produkty (np. Pobieranie niektórych publicznie dostępnych danych). Ta odpowiedź potwierdza moje podejrzenie, które no-corsnie jest powszechne, ponieważ jej OpaqueResponse nie jest zbyt przydatna; tj. „bardzo ograniczone przypadki”; Czy ktoś może mi wyjaśnić przykłady, gdzie no-corsjest przydatne?
The Red Pea
1
@TheRedPea Zobacz aktualizację odpowiedzi, którą wprowadziłem tutaj (i którą dodałem również jako komentarze do twojego pytania na stackoverflow.com/questions/52569895/ ... )
sideshowbarker
Musiałem skonfigurować mój serwer Express z pakietem CORS.js - github.com/expressjs/cors - a następnie musiałem usunąć mode: 'no-cors'z żądania pobierania (w przeciwnym razie odpowiedź byłaby pusta)
James L.
proxy CORS jest świetne. Jestem zdumiony, że blokowanie dostępu do plików publicznych jest uważane za środek „bezpieczeństwa”. Jest to tak łatwe do obejścia z perspektywy hakera i dokładnie to robi. To naprawdę kłopot dla dobrych aktorów.
Seph Reed
Generuję kod różnych klientów korzystających z tego samego API. Używam go do treningów. Chcę, aby kod był prosty i pozwolił użytkownikom bawić się nim. Muszę używać no-cros.
profimedica
6

Więc jeśli jesteś podobny do mnie i tworzysz stronę internetową na hoście lokalnym, gdzie próbujesz pobrać dane z Laravel API i używać ich w swoim interfejsie Vue, i widzisz ten problem, oto jak go rozwiązałem:

  1. W swoim projekcie Laravel uruchom polecenie php artisan make:middleware Cors. To stworzy app/Http/Middleware/Cors.phpdla Ciebie.
  2. Dodaj następujący kod wewnątrz handlesfunkcji w Cors.php:

    return $next($request)
        ->header('Access-Control-Allow-Origin', '*')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  3. W app/Http/kernel.php, dodaj następujący wpis w $routeMiddlewaretablicy:

    cors => \App\Http\Middleware\Cors::class

    (Nie byłoby inne wpisy w tablicy jak auth, guestitd. Również upewnić się, że robisz to na app/Http/kernel.phpponieważ nie ma innej kernel.phpteż w laravel)

  4. Dodaj to oprogramowanie pośredniczące do rejestracji tras dla wszystkich tras, dla których chcesz zezwolić na dostęp, na przykład:

    Route::group(['middleware' => 'cors'], function () {
        Route::get('getData', 'v1\MyController@getData');
        Route::get('getData2', 'v1\MyController@getData2');
    });
  5. W interfejsie Vue upewnij się, że wywołujesz ten interfejs API w mounted()funkcji, a nie w data(). Upewnij się również, że używasz adresu URL w rozmowie http://lub https://z adresem URL fetch().

Pełne uznanie dla artykułu na blogu Pete Houston .

dotNET
źródło
2

Proste rozwiązanie: dodaj poniższy tekst na samej górze pliku php, z którego żądasz danych.

header("Access-Control-Allow-Origin: *");

Naprawdę niezły kod
źródło
8
To bardzo dobry pomysł, jeśli zależy Ci na jak najmniejszym zabezpieczeniu :).
Heretic Monkey
co z
hotlink do
1

Rozwiązaniem dla mnie było po prostu zrobić to po stronie serwera

Użyłem WebClientbiblioteki C #, aby pobrać dane (w moim przypadku były to dane obrazu) i odesłać je do klienta. Prawdopodobnie jest coś bardzo podobnego w wybranym języku po stronie serwera.

//Server side, api controller

[Route("api/ItemImage/GetItemImageFromURL")]
public IActionResult GetItemImageFromURL([FromQuery] string url)
{
    ItemImage image = new ItemImage();

    using(WebClient client = new WebClient()){

        image.Bytes = client.DownloadData(url);

        return Ok(image);
    }
}

Możesz dostosować go do dowolnego własnego przypadku użycia. Główny punkt client.DownloadData()działa bez błędów CORS. Zwykle problemy z CORS występują tylko między witrynami internetowymi, dlatego wysyłanie żądań „między witrynami” z serwera jest w porządku.

Następnie wywołanie pobierania React jest tak proste, jak:

//React component

fetch(`api/ItemImage/GetItemImageFromURL?url=${imageURL}`, {            
        method: 'GET',
    })
    .then(resp => resp.json() as Promise<ItemImage>)
    .then(imgResponse => {

       // Do more stuff....
    )}
Stuart Aitken
źródło
1

Bardzo prostym rozwiązaniem (2 minuty do konfiguracji) jest użycie pakietu local-ssl-proxy znpm

Użycie jest całkiem proste:
1. Zainstaluj pakiet: npm install -g local-ssl-proxy
2. Podczas uruchamiania local-servermaski z rozszerzeniemlocal-ssl-proxy --source 9001 --target 9000

PS: Wymienić --target 9000z -- "number of your port"oraz --source 9001z--source "number of your port +1"

volna
źródło
1
Mam problem z używaniem mojej aplikacji w prawdziwym telefonie. Czy Twoje rozwiązanie jest pomocne w tym przypadku?
Hamid Araghi
@HamidAraghi przypuszczam, że teraz, skoro dla celów programistycznych serwer proxy powinien działać na urządzeniu, na którym faktycznie występuje błąd
volna