Итак, я пытаюсь загрузить данные из источника 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?
Обнаружив это через много часов, теперь он работает отлично. Тем не менее, я не могу понять, что такое CORS, кроме того, что крайне болезненно иметь скрытую ловушку, требующую много времени, чтобы выяснить, что это такое, при этом не обеспечивая дополнительной безопасности. Так что, если кто-то захочет дать на это ответ, я с радостью приму ответ.
@PaulSamsotha Ссылка решила проблему с точки зрения серверной части. Понадобилось пару часов, чтобы разобраться, но сейчас работает как шарм.
@PaulSamsotha Все еще есть проблема. Каждый раз, когда я обращаюсь к REST api через приложение Angular, я теряю сеанс. При каждом доступе сеанс возобновляется. Пробовал withCredentials: true на стороне клиента и сервера, но не получилось. Это происходит только тогда, когда URL-адрес отличается. Если URL такой же, сеанс работает отлично. Это снова CORS?
Я не знаю. Обычно я никогда не использую сеансы при работе с REST API и SPA, и это все, с чем я работаю. Так что у меня не так много рабочих знаний, что касается работы с сессиями.
Думаю, этот адрес вам больше поможет: stackoverflow.com/a/53587709




Проблема связана не с самим 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');
Разрешение должно быть с сервера. попробуйте заменить строку в Java-функции getReport .header («Access-Control-Allow-Methods», «GET, POST, DELETE, PUT») на .header («Access-Control-Allow-Methods», «GET, POST, DELETE, PUT, OPTIONS ")
Следующие шаги помогли мне решить эту проблему:
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 не предназначен для производства
Я решил проблему CORS с помощью прокси.
{
"/api/*": {
"target": "http://external-domain-you-wanted-to-access",
"secure": false,
"changeOrigin": true
}
}proxy.conf.json не предназначен для производства
Я почти целый день занимался решением проблемы. Сначала я подумал, что проблема связана с угловой стороной, но похоже, что проблема была со стороны сервера. Вам нужно добавить аннотацию @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();
});
@PaulSamsotha Не дубликат. Выяснил это. Проблема здесь в
ng serve, который должен быть настроен с--proxy-config proxy.config.json. В файле конфигурации должна быть настройка для"changeOrigin": true.