Angular 6 не получает доступ к REST с помощью Access-Control-Allow-Origin

Итак, я пытаюсь загрузить данные из источника REST в свое приложение Angular 6, используя http: HttpClient из '@angular/common/http'. Однако вызов приложения в браузере с помощью ng serve --open не работает. Я предполагаю, что проблема здесь в CORS. Я предполагаю, что мне нужно либо настроить сервер, либо клиентские заголовки с помощью Access-Control-Allow-Origin или что-то в этом роде, но я уже пробовал несколько способов без каких-либо успехов в том, чтобы заставить этот простой вызов REST работать. Вот что я закодировал ниже.

При вызове приложения Angular в браузере возникает следующая ошибка:

Failed to load http://localhost:8080/mysite-backend/rest/report/single/d83badf3: 
Response to preflight request doesn't pass access control check: 
No 'Access-Control-Allow-Origin' header is present on the requested 
resource. Origin 'http://localhost:4200' is therefore not allowed 
access.

Однако вызов того же URL-адреса (http://localhost:8080/mysite-backend/rest/report/single/d83badf3) в браузере Chrome работает отлично:

{"id":"d83badf3","language":"en","categoryId":"a5","title":"Simcity","created":1527723183880,"modified":1527723183880}

В Angular 6 я использую следующий класс обслуживания, который я создал с помощью ng generate service tour. В рамках этого я создал метод getTour(id: string), который не более чем вызывает REST по URL-адресу и возвращает полученную строку JSON в виде объекта Tour:

import { Injectable } from '@angular/core';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { Tour } from './tour';
import { catchError, tap } from 'rxjs/operators';

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json'
  })
};

@Injectable({
  providedIn: 'root'
})
export class TourService {

  // URL to the web api.
  private tourUrl = 'http://localhost:8080/mysite-backend/rest/report';

  constructor(
    private http: HttpClient
  ) { }

  /**
   * 
   * @param id: string GET tour report by id. Will 404 if id not found.
   */
  getTour(id: string): Observable<Tour> {
    httpOptions.headers.append('Access-Control-Allow-Origin', 'http://localhost:8080');
    httpOptions.headers.append('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
    httpOptions.headers.append('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
    httpOptions.headers.append('Access-Control-Allow-Credentials', 'true');

    const url = `${this.tourUrl}/single/${id}`;
    console.info("XXX URL GET " + url)
    return this.http.get<Tour>(url, httpOptions).pipe(
      tap(_ => this.log(`fetched tour id=${id}`)),
      catchError(this.handleError<Tour>(`getHero id=${id}`))
    );
  }

  private handleError<T> (operation = 'operation', result?: T) {
    return (error, any): Observable<T> => {
      console.error(error);
      this.log(`${operation} failed: ${error.message}`);
      return of(result as T);
    };
  }

  private log(message: string) {
    console.info("Log message: " + message);
  }
}

Это объект Tour:

export class Tour {
    id: string;
    language: string;
    categoryId: string;
    created: Date;
    modified: Date;
    title: string;
    content: string;
}

Я также добавил HttpClientModule из '@angular/common/http' в массив imports и массив providers в app.module.ts.

RESTful WebService построен на Java. Здесь у меня есть метод getReport(@PathParam("reportId") String reportId), который получает объект Report и возвращает его в Reponse:

import java.util.List;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

/**
 * Describes the RESTful access for reports.
 */
@Path("/report")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReportResource {
    @Inject
    private Logger logger;

    @GET
    @Path("/single/{reportId}")
    public Response getReport(@PathParam("reportId") String reportId) {
        //return Mock.getReport(reportId);
        return Response.ok() // 200
                       .entity(Mock.getReport(reportId))
                       .header("Access-Control-Allow-Origin", "*")
                       .header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
                       .allow("OPTIONS").build();
    }
...
}

Что мне нужно сделать, чтобы совершить успешный вызов от клиента Angular 6 к Java RESTful WebService?

@PaulSamsotha Не дубликат. Выяснил это. Проблема здесь в ng serve, который должен быть настроен с --proxy-config proxy.config.json. В файле конфигурации должна быть настройка для "changeOrigin": true.

Socrates 31.05.2018 16:42

Обнаружив это через много часов, теперь он работает отлично. Тем не менее, я не могу понять, что такое CORS, кроме того, что крайне болезненно иметь скрытую ловушку, требующую много времени, чтобы выяснить, что это такое, при этом не обеспечивая дополнительной безопасности. Так что, если кто-то захочет дать на это ответ, я с радостью приму ответ.

Socrates 31.05.2018 16:45
stackoverflow.com/a/50241292/2587435
Paul Samsotha 31.05.2018 16:51

@PaulSamsotha Ссылка решила проблему с точки зрения серверной части. Понадобилось пару часов, чтобы разобраться, но сейчас работает как шарм.

Socrates 02.06.2018 02:37

@PaulSamsotha Все еще есть проблема. Каждый раз, когда я обращаюсь к REST api через приложение Angular, я теряю сеанс. При каждом доступе сеанс возобновляется. Пробовал withCredentials: true на стороне клиента и сервера, но не получилось. Это происходит только тогда, когда URL-адрес отличается. Если URL такой же, сеанс работает отлично. Это снова CORS?

Socrates 02.06.2018 03:05

Я не знаю. Обычно я никогда не использую сеансы при работе с REST API и SPA, и это все, с чем я работаю. Так что у меня не так много рабочих знаний, что касается работы с сессиями.

Paul Samsotha 02.06.2018 04:55

Думаю, этот адрес вам больше поможет: stackoverflow.com/a/53587709

ilmaz 10.06.2019 10:16
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
6
7
47 317
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Проблема связана не с самим angular, а с используемым вами веб-сервером.

Клиентский запрос angular http всегда имеет предварительный запрос с типом параметры перед тем, как выполнить фактический запрос.

вам может потребоваться добавить метод запроса OPTIONS в эту строку

   .header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")

Как вы думаете, куда мне добавить OPTIONS? Я спрашиваю, потому что у меня уже есть ОПЦИИ, добавленные в мой клиентский вызов Angular: httpOptions.headers.append('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

Socrates 31.05.2018 02:13

Разрешение должно быть с сервера. попробуйте заменить строку в Java-функции getReport .header («Access-Control-Allow-Methods», «GET, POST, DELETE, PUT») на .header («Access-Control-Allow-Methods», «GET, POST, DELETE, PUT, OPTIONS ")

Ihab Salem 31.05.2018 03:54
stackoverflow.com/questions/50614896/…
Agnel Amodia 06.02.2020 01:18

Следующие шаги помогли мне решить эту проблему:

1- создать файл proxy.conf.json в корне вашего проекта вместо папки src

3- Добавьте код, как показано ниже, в файл proxy.conf.json.

{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

4- добавить именно это в package.json под скриптами "start": ng serve --proxy-config proxy.conf.json

5- делать npm start вместо ng serve

proxy.conf.json не предназначен для производства

jcdsr 03.10.2019 15:33

Я решил проблему CORS с помощью прокси.

  1. Создайте файл proxy.conf.json на том же уровне, что и package.json.
  2. добавить следующий контент

{
    "/api/*": {
        "target": "http://external-domain-you-wanted-to-access",
        "secure": false,
        "changeOrigin": true
    }
}
  1. Запускаем команду ng serve --proxy-config proxy.conf.json

Ссылка на ссылку

proxy.conf.json не предназначен для производства

jcdsr 03.10.2019 15:33

Я почти целый день занимался решением проблемы. Сначала я подумал, что проблема связана с угловой стороной, но похоже, что проблема была со стороны сервера. Вам нужно добавить аннотацию @CrossOrigin над методом get.

@GET
@CrossOrigin(origins = "http://localhost:4200")
@Path("/single/{reportId}")
public Response getReport(@PathParam("reportId") String reportId) {
    //return Mock.getReport(reportId);
    return Response.ok() // 200
                   .entity(Mock.getReport(reportId))
                   .header("Access-Control-Allow-Origin", "*")
                   .header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
                   .allow("OPTIONS").build();
}

Я потратил почти день на выяснение клиентского приложения angular, но на самом деле проблема была в приложении nodejs на стороне сервера, чтобы разрешить CORS.

app.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); });

Другие вопросы по теме