Como consumir um WebService SOAP no Angular 7

Galera, estou tentando consumir um WebService SOAP no Angular 7, estou usando o ngx-soap na versão 0.6.3, mas não estou conseguindo, o projeto roda mas quando tento acessar o recurso o navegador emite este erro no console:

ERROR TypeError: Cannot read property 'subscribe' of undefined
at RegistroComponent.push../src/app/correspondencia/registro/registro.component.ts.RegistroComponent.pesquisarSEI (registro.component.ts:183)
at Object.eval [as handleEvent] (RegistroComponent.html:76)
at handleEvent (core.js:23107)
at callWithDebugContext (core.js:24177)
at Object.debugHandleEvent [as handleEvent] (core.js:23904)
at dispatchEvent (core.js:20556)
at core.js:21003
at HTMLButtonElement.<anonymous> (platform-browser.js:993)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423)
at Object.onInvokeTask (core.js:17290)

Meu método:

public consultarProcesso(numeroProcesso?: string): Observable<ConsultarProcedimentoResponse> {

        if (numeroProcesso == undefined || numeroProcesso == null) {

            return new Observable<ConsultarProcedimentoResponse>();

        } else {

            this.soap.createClient('assets/ws_sei.wsdl')

                .then(client => {

                    let cliente: any = client;

                    let request: ConsultarProcedimentoRequest = {

                        SiglaSistema: 'SIPESWEB',

                        IdentificacaoServico: 'consultaProcessos',

                        IdUnidade: null,

                        ProtocoloProcedimento: numeroProcesso,

                        SinRetornarAssuntos: 'S',

                        SinRetornarInteressados: 'S',

                        SinRetornarObservacoes: 'S',

                        SinRetornarAndamentoGeracao: 'N',

                        SinRetornarAndamentoConclusao: 'N',

                        SinRetornarUltimoAndamento: 'N',

                        SinRetornarUnidadesProcedimentoAberto: 'N',

                        SinRetornarProcedimentosRelacionados: 'N',

                        SinRetornarProcedimentosAnexados: 'N'

                    }

                    return cliente.consultarProcedimento(request)

                        .subscrible(response => {

                            return response as Observable<ConsultarProcedimentoResponse>;

                        }, catchError(this.handleError),

                            publishReplay(1),

                            refCount()

                        )

                });

        }

    }

Se alguém já tiver trabalhado com essa biblioteca, me dê um help!!!

Desde já agradeço a todos!

Nunca mexi com essa lib, mas isso aqui está estranho para mim. Pq isso não está no construtor? É lá onde deve ser feita a injeção de dependência.

Veja o exemplo na página do NPM: https://www.npmjs.com/package/ngx-soap/v/0.6.3

O correto aqui é subscribe.

Já está no construtor da classe, apenas repassei o método que gera o erro, a injeção está funcionando corretamente.

Já atualizei isso, mas continua o mesmo erro!

@gbuenoo segui seu conselho e melhorei meu código, agora ficou assim:

constructor(soap: NgxSoapService) {

        super();

        soap.createClient('assets/ws_sei.wsdl', ({ method: 'GET', disableCache: true }), SEI_WS).then(client => this._client = client);

    }

    public consultarProcesso(numeroProcesso: string): ConsultarProcedimentoResponse {

        var resposta: ConsultarProcedimentoResponse = null;

        let request = this.montarRequest(numeroProcesso);

        this._client.call('consultarProcedimento', request, {token: this.getResourceOwner().token}).subscribe(

            response => {

                resposta = this.montarResponse(response);

            }, err => {

                throw err;

            }

        );

        return resposta;

    }

Mas agora o erro apresentado é outro:
Access to XMLHttpRequest at 'https://sei.desenv.unb.br/sei/controlador_ws.php?servico=sei' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
1. HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: “Unknown Error”, url: “https://sei.desenv.unb.br/sei/controlador_ws.php?servico=sei”, ok: false, …}

  1. error: ProgressEvent

    1. bubbles: false
    2. cancelBubble: false
    3. cancelable: false
    4. composed: false
    5. currentTarget: XMLHttpRequest {__zone_symbol__xhrSync: false, __zone_symbol__xhrURL: "https://sei.desenv.unb.br/sei/controlador_ws.php?servico=sei", __zone_symbol__loadfalse: Array(1), __zone_symbol__errorfalse: null, __zone_symbol__xhrScheduled: true, …}
    6. defaultPrevented: false
    7. eventPhase: 0
    8. isTrusted: true
    9. lengthComputable: false
    10. loaded: 0
    11. path: []
    12. returnValue: true
    13. srcElement: XMLHttpRequest {__zone_symbol__xhrSync: false, __zone_symbol__xhrURL: "https://sei.desenv.unb.br/sei/controlador_ws.php?servico=sei", __zone_symbol__loadfalse: Array(1), __zone_symbol__errorfalse: null, __zone_symbol__xhrScheduled: true, …}
    14. target: XMLHttpRequest {__zone_symbol__xhrSync: false, __zone_symbol__xhrURL: "https://sei.desenv.unb.br/sei/controlador_ws.php?servico=sei", __zone_symbol__loadfalse: Array(1), __zone_symbol__errorfalse: null, __zone_symbol__xhrScheduled: true, …}
    15. timeStamp: 267833.9899999846
    16. total: 0
    17. type: "error"
    18. __proto__: ProgressEvent

  2. headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, headers: Map(0)}
  3. message: "Http failure response for https://sei.desenv.unb.br/sei/controlador_ws.php?servico=sei: 0 Unknown Error"
  4. name: "HttpErrorResponse"
  5. ok: false
  6. status: 0
  7. statusText: "Unknown Error"
  8. url: "https://sei.desenv.unb.br/sei/controlador_ws.php?servico=sei"
  9. __proto__: HttpResponseBase

A aplicação cria o cliente, mas não realiza a consulta, acredito se seja esse erro de CORS…

Nao pode criar uma api REST pra consumir esse soap no seu backend e retornar o resultado em json pro client?

@javaflex eu não queria ter que fazer isso, tenho um backend em java que poderia fazer isso mas teria que desenvolver o acesso ao SOAP de qualquer jeito, além de estar criando uma camada adicional ao projeto, fazendo um acesso ao backend, pra que fizesse um acesso ao serviço pra buscar a informação, sendo que poderia acessar diretamente o serviço.

Na prática querer isso seria preciosismo. É muito mais fácil/mais disseminado consumir SOAP pelo backend Java. Além das regras de acesso as informações desse web service de terceiro não ficarem totalmente expostas no browser do usuário. No backend poderá ter todo um controle.

1 curtida

Cara, agora seu problema é com o Webservice. Terá de ler as documentações e/ou solicitar acesso a quem mantém o serviço.

@Bambatera,

Se for uma integração com um sistema de terceiros ou até mesmo um legado, eu concordo plenamente com isso.

@gbuenoo, já estou providenciando o contato, vlw!

1 curtida

Não quero fazer esse tipo de acesso ao back-end pois, atualmente, nosso back-end está utilizando uma arquitetura obsoleta, já iniciamos um projeto de mudança da arquitetura, já foi aprovado pela diretoria, mas ainda não foi iniciado, não gostaria de ter que refazer isso quando mudar a arquitetura, ainda mais sendo que o acesso é a um sistema de terceiros.

1 curtida

Nao precisa mudar arquitetura. Basta criar classes isoladas que façam o consumo desse SOAP no Java.

Não é nada do outro mundo, o resultado dessa classe isolada vai ser um DTO. A partir daí responde em json. Não tem pq ficar nessa complicação.

Pode usar o próprio Eclipse ou Netbeans pra fazer boa parte desse trabalho pra você:

IMG_20201016_103949_207
Informa o endereço do wsdl:
IMG_20201016_103853_583

https://m.youtube.com/results?search_query=java+web+service+client

Com o intuito de fechar esse tópico, e documentar a solução pra futuras dúvidas, um amigo e eu resolvemos o problema da seguinte forma:

Lembrando que todos os procedimento foram realizados apenas no front-end.

Inicialmente criamos um arquivo json chamado proxy.conf.json com este conteúdo:

{

"/sipesweb/sei": {

    "target": "https://sei.homologa.unb.br/sei/ws/SeiWS.php",

    "secure": false,

    "logLevel": "debug",

    "pathRewrite": { "^/sipesweb/sei": "" },

    "changeOrigin": true

}

}

Criamos uma função assíncrona pra tratar a resposta da requisição:

public async pesquisarSEI() {

    try {

        if (this._correspondenciaSelecionada.unbDoc.trim().length > 0) {

            await this._seiService.consultarProcesso(this._correspondenciaSelecionada.unbDoc.trim()).then(async response => {

                if (await response) {

                    console.log(JSON.stringify(response));

                    this._correspondenciaSelecionada.unbDoc = response.ProcedimentoFormatado;

                    this._correspondenciaSelecionada.assunto = response.Especificacao.toUpperCase();

                    this._correspondenciaSelecionada.favorecido = ''; //response.Interessados[0].Nome.toUpperCase();

                } else {

                    this._correspondenciaService.showMessage('Não existe processo com o número informado!');

                }

            });

        } else {

            this._correspondenciaService.showMessage('Por favor, informe um número de Processo!');

        }

    } catch (error) {

        catchError(this._correspondenciaService.handleError);

        this._correspondenciaService.showMessage(error.message);

    }

}
Criamos uma função, também assíncrona, pra realizar a consulta das informações:
/**
     * Realiza a consulta de processos no SEI.
     * 
     * @param numeroProcesso Número do processo a ser pesquisado.
     * @returns Promise da interface ConsultarProcedimentoResponse
     */
    public async consultarProcesso(numeroProcesso: string): Promise<ConsultarProcedimentoResponse> {

        let procedimentoResposta;

        try {

            const request = this.montarRequest(numeroProcesso); // Monta o conteúdo da requisição.

            console.log('Mensagem montada!');

            console.log('Estabelecendo conexão com o serviço SOAP...');

            await this._soap.createClient('assets/ws_sei.wsdl').then( // Estabelece a conexão com o servidor SOAP.

                async client => {

                    this._client = client;

                    if (this._client) {

                        console.log('Conexão estabelecida!');

                        console.log('Encaminhando mensagem...');

                        /**

                         * A função consultarProcedimento() é o serviço oferecido no WSDL para a consulta a ser realizada, 

                         * essa função recebe, obrigatoriamente, o conteúdo da requisição (envelope).

                         */

                        await this._client.consultarProcedimento(request).toPromise().then(async respostaRequisicao => {                            

                            await this.montarResponse(respostaRequisicao).then(respostaMontada => {

                                procedimentoResposta = respostaMontada;

                                console.log('Resposta recebida!');

                            });

                        });

                    } else {

                        throw 'Não foi possível estabelecer a conexão com o serviço!';

                    }

                });

        } catch (error) {

            throw error;

        } finally {

            return procedimentoResposta;

        }

    }

Criamos uma função para montar o envelope de requisição:

/**
     * Monta o envelope de requisição para o protocolo SOAP.
     * 
     * @param numeroProcesso Número do processo a ser pesquisado.
     * @returns Objeto baseado na interface ConsultarProcedimentoRequest.
     */
    private montarRequest(numeroProcesso: string): ConsultarProcedimentoRequest {

        try {

            console.log('Montando mensagem de requisição...');

            return {

                SiglaSistema: 'SIPESWEB', // Nome do sistema registrado.

                IdentificacaoServico: 'consultarProcesso', // WebService a ser utilizado.

                IdUnidade: '', // Filtro

                ProtocoloProcedimento: numeroProcesso, // Número do processo a ser pesquisado.

                SinRetornarAssuntos: 'S',                   //

                SinRetornarInteressados: 'S',               //

                SinRetornarObservacoes: 'S',                //

                SinRetornarAndamentoGeracao: 'N',           //

                SinRetornarAndamentoConclusao: 'N',         // Configurações adicionais

                SinRetornarUltimoAndamento: 'N',            //

                SinRetornarUnidadesProcedimentoAberto: 'N', //

                SinRetornarProcedimentosRelacionados: 'N',  //

                SinRetornarProcedimentosAnexados: 'N'       //

            }

        } catch (error) {

            throw error;

        }

    }

Criamos uma função para realizar o parse de XML para JSON:

/*
* Realiza o parse do XML obtido na resposta para JSON.
*
* @param respostaRequisicao Response obtido pela pesquisa realizada.
* @returns Promise baseada na interface ConsultarProcedimentoResponse.
*/
private async montarResponse(respostaRequisicao): Promise {

    try {

        let parser = new DOMParser();

        let xml = parser.parseFromString(respostaRequisicao.responseBody.toString(), 'application/xml');

        console.log('Obtendo objeto resposta...');

        return {

            IdProcedimento: xml.getElementsByTagName('IdProcedimento')[0].childNodes[0].nodeValue,

            ProcedimentoFormatado: xml.getElementsByTagName('ProcedimentoFormatado')[0].childNodes[0].nodeValue,

            Especificacao: xml.getElementsByTagName('Especificacao')[0].childNodes[0].nodeValue,

            DataAutuacao: xml.getElementsByTagName('DataAutuacao')[0].childNodes[0].nodeValue,

            LinkAcesso: xml.getElementsByTagName('LinkAcesso')[0].childNodes[0].nodeValue,

            TipoProcedimento: this.montarTipoProcedimento(xml.getElementsByTagName('TipoProcedimento')),

            AndamentoGeracao: this.montarAndamento(xml.getElementsByTagName('AndamentoGeracao')),

            AndamentoConclusao: this.montarAndamento(xml.getElementsByTagName('AndamentoConclusao')),

            UltimoAndamento: this.montarAndamento(xml.getElementsByTagName('UltimoAndamento')),

            UnidadesProcedimentoAberto: this.carregarListaUnidades(xml.getElementsByTagName('UnidadesProcedimentoAberto')),

            Assuntos: this.carregarListaAssuntos(xml.getElementsByTagName('Assuntos')),

            Interessados: this.carregarListaInteressados(xml.getElementsByTagName('Interessados')),

            Observacoes: this.carregarListaObservacoes(xml.getElementsByTagName('Observacoes')),

            ProcedimentosRelacionados: this.carregarListaProcedimentos(xml.getElementsByTagName('ProcedimentosRelacionados')),

            ProcedimentosAnexados: this.carregarListaProcedimentos(xml.getElementsByTagName('ProcedimentosAnexadoss'))

        }

    } catch (error) {

        throw error;

    }

}
E chamamos a pesquisa em um botão diretamente na página web.

<button id="btnPesquisa" matSuffix mat-icon-button color="primary" title="Pesquisar SEI" aria-label="Pesquisar SEI" (click)="pesquisarSEI()" type="button"><mat-icon>search</mat-icon></button>

Com isso, conseguimos acessar diretamente o SOAP e tratar as informações recebidas de acordo com a necessidade.

Desde já agradeço a opnião de todos.

2 curtidas