Приложение Angular, развернутое в nginx на докере, не подключается к API-интерфейсу java. Метод GET возвращает html вместо объекта, POST, PUT, PATCH имеет 405 Not Allowed
У меня есть рабочее приложение angular * с backend api (загрузка java spring). Angular-приложение, работающее в среде IDE, уже настроило proxy.conf.json и работает с Chrome cors, передавая любой запрос бэкэнду. Проблема началась, когда я развернул приложение angular на nginx (пробовал 1.15.0, 1.16.0, 1.17.0) в докере. Ни одна из операций http.client работать не хочет. GET возвращает http-код вместо json / javascript-объекта. POST, PATCH, PUT возвращает 405 Not allowed.
Похоже, что проблема связана с неподходящей конфигурацией nginx, но, с другой стороны, я протестировал множество конфигураций обратного прокси и cors с тем же результатом ...
Приложение Angular: http://localhost:8100 (запускается из докера) Spring boot api: http://localhost:8081 (работает из IDE)
угловая сборка с помощью команды:
ng build --prod
dockerfile:
FROM nginx:1.17.0
COPY nginx.conf /etc/nginx/nginx.conf
COPY prm-web-app/ usr/share/nginx/html/
EXPOSE 8100
nginx.conf:
http {
include mime.types;
default_type application/json;
sendfile on;
keepalive_timeout 65;
upstream api_server {
server localhost:8081;
}
server {
listen 8100;
server_name example;
index index.html index.htm;
root /usr/share/nginx/html;
location / {
add_header Allow 'GET, POST, PUT, PATCH, DELETE' always;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, PATCH, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
try_files $uri $uri/ /index.html;
}
location /api {
add_header Allow 'GET, POST, PUT, PATCH, DELETE' always;
proxy_pass http://api_server;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
proxy_set_header Host $http_host;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
угловые дополнения для примеров:
private extractData(res: Response) {
const body = res;
return body || { };
}
private httpHeaders = new HttpHeaders({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Origin, X-Request-With, Content-type, Accept, X-Access-Token',
'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH, OPTIONS'
});
angular пример операции GET:
getAppointment(id: string): Observable<any> {
return this.http.get(apiEndpoint + '/' + id).pipe(
map(this.extractData),
catchError( err => {
this.logger.logError(ErrorLevel.ERROR, JSON.stringify(err));
err = this.handleError(err);
return throwError(err);
})
);
angular пример операции POST:
postSearchTimeSlot(searchTimeSlotPostRequest: SearchTimeSlotPostRequest): Observable<any> {
const options = {headers: this.httpHeaders};
return this.http.post(searchTimeSlotEndpoint + '/', searchTimeSlotPostRequest, options).pipe(
map(this.extractData),
catchError(err => {
this.logger.logError(ErrorLevel.ERROR, JSON.stringify(err));
err = this.handleError(err);
return throwError(err);
})
);
}
Я пробовал такие решения, как:
error_page 405 =200 $uri;
а также:
error_page 405 = @app;
location @app {
proxy_pass http://localhost:8081/api;
}
и: добавлено приложение / json в mime.types и:
const options = {headers: this.httpHeaders, responseType: 'json' as 'json'};
const options = {headers: this.httpHeaders, responseType: 'text' as 'json'};
а также:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Bean
public WebMvcConfigurer corsConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("PUT", "POST", "PATCH", "GET")
.allowCredentials(true).maxAge(3600);
}
};
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
и несколько других с proxy_pass и add_header для nginx. Сейчас уже идей не хватает ...
Каждый раз возникают одни и те же проблемы (проверено на последних версиях chrome и mozilla firefox): для запроса GET:
{
"headers": {
"normalizedNames": {},
"lazyUpdate": null
},
"status": 200,
"statusText": "OK",
"url": "http://localhost:8100/appointment_api/api/v1/appointment/123412341234",
"ok": false,
"name": "HttpErrorResponse",
"message": "Http failure during parsing for http://localhost:8100/appointment_api/api/v1/appointment/123412341234",
"error": {
"error": {},
"text": "<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.ab8e79b2d91e978b1e50.css"></head>
<body>
<app-root></app-root>
<script src="runtime-es2015.858f8dd898b75fe86926.js" type="module"></script><script src="polyfills-es2015.b8312458afdf3a7f4969.js" type="module"></script><script src="runtime-es5.741402d1d47331ce975c.js" nomodule></script><script src="polyfills-es5.8de8aa8baf008bce96d4.js" nomodule></script><script src="scripts.99edbfa41b8b40270fe5.js"></script><script src="main-es2015.49e5dbe84808fb269a5d.js" type="module"></script><script src="main-es5.628bd9db49e6b16c7a86.js" nomodule></script></body>
</html>
"
}
}
приведенный выше код ошибки в браузере связан с тем, что angular по умолчанию ожидает json, но nginx возвращает html instad json.
для запроса PUT, POST, PATCH:
"status": 405,
"statusText": "Not Allowed",
"url": "http://localhost:8100/appointment_api/api/v1/searchTimeSlot/",
"ok": false,
"name": "HttpErrorResponse",
"message": "Http failure response for http://localhost:8100/appointment_api/api/v1/searchTimeSlot/: 405 Not Allowed",
"error": "<html>
<head><title>405 Not Allowed</title></head>
<body>
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx/1.17.0</center>
</body>
</html>
По обеим вопросам не знаю, что виноват и как это исправить.