Контроллер Spring REST не отвечает на запрос Angular

У меня есть приложение для создания запросов сертификатов сервера, как если бы вы использовали java keytool или что-то в этом роде. Я пытаюсь вернуть созданный запрос сертификата и ключ в zip-файле, но, хоть убей, я не могу заставить свой REST-контроллер отвечать на HTTP-запрос. ИСПРАВЛЕНИЕ: контроллер отвечает, но код метода никогда не выполняется.

Сервер действительно получает запрос, потому что мой фильтр CORS выполняется. Но у меня есть отладка в методе контроллера, и она никогда не срабатывает. Подпись метода верна? Мне нужна еще пара глаз, пожалуйста?

Вот мой код контроллера:

@RequestMapping(method = RequestMethod.POST, value = "/generateCert/")
public ResponseEntity<InputStreamResource> generateCert(@RequestBody CertInfo certInfo) {
    System.out.println("Received request to generate CSR...");

    byte[] responseBytes = commonDataService.generateCsr(certInfo);
    InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(responseBytes));

    System.out.println("Generated CSR with length of " + responseBytes.length);
    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=certificate.zip")
            .contentType(MediaType.parseMediaType("application/zip"))
            .contentLength(responseBytes.length)
            .body(resource);
}

А вот и запрос Angular:

generateCertificate(reqBody: GenerateCert) {
   let headers = new Headers();
   headers.append('Content-Type', 'application/json');

   this.http.post(this.urlGenerateCert, JSON.stringify(reqBody), {headers: headers}).subscribe(
    (data) => {
        let dataType = data.type;
        let binaryData = [];
        binaryData.push(data);
        this.certBlob = new Blob(binaryData);
    });
    return this.certBlob;
 }

И, наконец, заголовки запроса и ответа, которые я скопировал из сетевой панели:

Response
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, X-Requested-With, remember-me
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3600
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Date: Thu, 27 Dec 2018 22:48:00 GMT
Expires: 0
Location: http://localhost:8102/login
Pragma: no-cache
Set-Cookie: JSESSIONID=EDACE17328628D579670AD0FB53A6F35; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

Request
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 205
Content-Type: application/json
Host: localhost:8102
Origin: http://localhost:4200
Referer: http://localhost:4200/generateCerts
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36

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

Это снова мы. http.post возвращает Observable <Blob>. Не клякса. Почему? Потому что AJAX асинхронный. Таким образом, он возвращает наблюдаемое, что позволяет вам получать уведомления намного позже, когда ответ будет наконец доступен. Когда вы запускаете this.certBlob, вы делаете это сразу после отправки запроса. Ответ еще не пришел. Итак, this.certBlob не определен. Сервис должен возвращать наблюдаемое. Компонент, заинтересованный в ответе, должен подписаться на возвращаемый наблюдаемый объект, чтобы получить уведомление о возвращении сертификата.

JB Nizet 28.12.2018 00:07

Умм, кажется, я подписался на запрос - пролистайте.

CNDyson 28.12.2018 00:12

Вы подписывайтесь. Сразу после того, как вы подписались и, таким образом, отправили запрос, вы возвращаете this.certBlob, который не определен, поскольку он будет заполнен гораздо позже, после того, как метод вернется в течение длительного времени, когда обратный вызов передан на подписку был вызван, потому что наконец-то пришел ответ от сервера. Нельзя есть тост сразу после того, как вы положили его в тостер. Вам нужно подождать, пока тостер не сообщит, что тосты приготовлены на гриле.

JB Nizet 28.12.2018 00:15

Не могли бы вы вместо этого поделиться тем, что я должен делать? Разработка пользовательского интерфейса - не моя специальность.

CNDyson 28.12.2018 00:16

Но имейте в виду, моя проблема в том, что серверная часть вообще не отвечает. Я еще даже не проверил файл.

CNDyson 28.12.2018 00:17

Я тебе уже сказал. Прочтите мой первый комментарий. Служба должна возвращать abservable. Т.е. return this.http.post(...); Компонент, который вызывает службу te и хочет получить доступ к сертификату, должен подписаться на наблюдаемое, возвращаемое службой. НЕ подписывайтесь на сервис.

JB Nizet 28.12.2018 00:18

Если сервер вообще не отвечает, почему в вашем вопросе указано 2 ответа?

JB Nizet 28.12.2018 00:20

Я должен был быть более конкретным - мой метод контроллера не вызывается.

CNDyson 28.12.2018 00:21

Тогда что именно происходит и каковы тогда ответы на ваш вопрос? Они вообще актуальны?

JB Nizet 28.12.2018 00:23

Вызывается фильтр сервлета, но запрос никогда не сопоставляется с методом контроллера. Подумал, может, с подписью что-то не так? И я неправильно набрал - это запрос и ответ, которые я получаю. Я подумал, что из них можно что-то почерпнуть.

CNDyson 28.12.2018 00:26

Начать с открытия сетевой панели в инструментах разработчика вашего браузера? Какие запросы отправляются? Если вы используете CORS, вы должны иметь OPTIONS, за которым следует POST. Убедитесь, что POST есть. Проверьте URL. (Почему вы используете CORS, BTW, действительно ли вы планируете открыть этот API для внешних приложений JavaScript?)

JB Nizet 28.12.2018 00:28

Позвольте нам продолжить обсуждение в чате.

CNDyson 28.12.2018 00:28

Нет, я не болтаю. Уточните пожалуйста свой вопрос.

JB Nizet 28.12.2018 00:28

Вопрос отредактирован!

CNDyson 28.12.2018 00:35

Вы до сих пор не сказали, был ли отправлен запрос POST, и какой был URL-адрес запроса. Если запрос POST не отправляется, это означает, что ваша конфигурация CORS неверна. И вы до сих пор не сказали, почему вы используете CORS.

JB Nizet 28.12.2018 00:38

Вы аннотировали свой внутренний метод generateCert с помощью @CrossOrigin?

TinyOS 28.12.2018 01:09

Я аннотировал класс, да.

CNDyson 28.12.2018 01:32

Почему бы не использовать REST-клиент, например Почтальон, чтобы сначала проверить конечную точку. Это поможет точно узнать, связана ли проблема с приложением angular или приложением spring -boot.

Olantobi 02.01.2019 21:50
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
18
3 896
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

определить proxy.conf.json

{
"/login*": {
    "target":"http://localhost:8080",
    "secure":false,
    "logLevel":"debug"
    }
} 

теперь в твоем package.json

"scripts": {
    "start":"ng serve --proxy-config proxy.config.json"
}

Я думаю, что есть проблема с подключением в обоих веб-приложениях. Попробуйте.

Когда Angular встречает это утверждение

this.http.post(url,body).subscribe(data => # some code
);

Он немедленно возвращается для выполнения остальной части кода, в то время как служба продолжает выполняться. Прямо как будущее на Java.

Здесь, если ты

return this.cert;

Вы не получите значение, которое в конечном итоге может быть заполнено службой this.http. Поскольку страница уже отрисована и код выполнен. Вы можете проверить это, включив это в Observable и вне его.

console.info(“Inside/outside observable” + new Date().toLocalTimeString());

В списке заголовков запроса / ответа отсутствует информация об URL-адресе, методе и наиболее важном коде статуса ответа.

Увидев Location: http://localhost:8102/login среди заголовков ответов, я могу предположить, что это может быть 401 Unauthorized или что-то еще, что перенаправляет на страницу входа. Следовательно, если в цепочке фильтров есть фильтр аутентификации, он может быть виновником.

Следующие заголовки запроса

Host: localhost:8102
Origin: http://localhost:4200

предполагает, что вы выполняете CORS, и фильтр CORS действительно может быть задействован и выполнить ответ до того, как запрос будет перенаправлен на контроллер. Я предлагаю установить точку останова в фильтре CORS (и в других фильтрах, если таковые имеются) и отладить ее до точки, в которой будет возвращен ответ.

Да, это именно то, что я сделал, и я обнаружил, что запрос поступает на сервер, но не на метод моего контроллера.

CNDyson 08.01.2019 22:52
Ответ принят как подходящий

Спасибо всем, кто внес свой вклад. Я обнаружил, что ошибка связана с заголовками моего метода контроллера. После их изменения метод был вызван правильно. Вот что сработало:

@RequestMapping(method = RequestMethod.POST, path = "/generateCert", 
    produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE}, consumes = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<byte[]> generateCert(@RequestBody CertInfo certInfo) {
    byte[] responseBytes = commonDataService.generateCsr(certInfo);    
    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
            .contentLength(responseBytes.length)
            .body(responseBytes);
}

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