Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre du Club
    Erreur cross origin : Uncaught (in promise): HttpErrorResponse - No 'Access-Control-Allow-Origin' header
    Bonjour,

    je développe une appli de gestion de produits sur angular 6 et spring boot 2.

    J'ai créé mon service qui appelle la liste de mes produits côté angular et côté spring boot le contrôleur associé avec l'annotation cross origin pour permettre mon front d'atteindre mon back.

    Malheureusement , quand je tape l'url : http://localhost:4200/produit, j'ai une erreur sur la console de mon navigateur : chrome, firefox, safari :

    Error: Uncaught (in promise): HttpErrorResponse: {"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown Error","url":null,"ok":false,"name":"HttpErrorResponse","message":"Http failure response for (unknown url): 0 Unknown Error","error":{"isTrusted":true}}
    at resolvePromise (zone.js:831)
    at resolvePromise (zone.js:788)


    voici mon service côté angular :
    Code js :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    export const API_URLS = {
        PRODUITS_URLS : 'http://localhost:8080/api/produit'
    }


    Code js :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import { Injectable } from "@angular/core";
    import { HttpClient, HttpHeaders } from "@angular/common/http";
    import { Observable } from "rxjs";
     
    import { API_URLS } from '../config/api.url.config';
    import { Produit } from "../shared/produit";
     
    const httpOptions = {
        headers: new HttpHeaders({ 'Content-Type': 'application/json' })
      };
     
    @Injectable()
    export class ProduitService {
     
        constructor(private http : HttpClient) {
     
        }
     
        getPoduits():Observable<any>{
            /*return this.http.get(API_URLS.PRODUITS_URLS);*/
            return this.http.get('/api/produit');
        }
     
        addProduit(produit : Produit):Observable<any>{
            return this.http.post('/api/produit',produit);
        }
     
        updateProduit(produit : Produit):Observable<any>{
            /*return this.http.put(API_URLS.PRODUITS_URLS,produit);*/
            return this.http.put('/api/produit',produit);
        }
     
        deleteProduit(id : number):Observable<any>{
            return this.http.delete(`/api/produit/${id}`);
        }
    }


    Code js :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    import { Component, OnInit } from '@angular/core';
    import { Produit } from '../shared/produit';
    import { FormGroup, FormBuilder, Validators } from '@angular/forms';
    import { ProduitService } from './produit.service';
    import { ActivatedRoute } from '@angular/router';
     
    @Component({
      selector: 'app-produit',
      templateUrl: './produit.component.html',
      styleUrls: ['./produit.component.css']
    })
    export class ProduitComponent  implements OnInit {
     
      produitForm : FormGroup;
     
      products<img src="images/smilies/icon_razz.gif" border="0" alt="" title=":P" class="inlineimg" />roduit[];
     
      operation : string = 'add';
     
      selectedProduit : Produit;
     
      constructor(private produitService<img src="images/smilies/icon_razz.gif" border="0" alt="" title=":P" class="inlineimg" />roduitService, private fb : FormBuilder,
        private route : ActivatedRoute) {
        this.createForm();
      }
     
      ngOnInit() {
        /*this.products = this.produitService.getPoduits();*/
        this.initProduit();
       // this.loadProduits();
        this.products = this.route.snapshot.data.produits;
      }
     
      createForm(){
        this.produitForm = this.fb.group({
          ref:['', Validators.required],
          quantite:'',
          prixUnitaire:''
        });
      }
     
      loadProduits() {
        this.produitService.getPoduits().subscribe(
          data => { this.products = data},
          error => { console.log("An error was occured.")},
          () => { console.log("loading products")}
        )
      }
     
      addProduit(){
        const p = this.produitForm.value;
        console.log("produit envoye ");
        console.log(p);
        this.produitService.addProduit(p).subscribe(
          res => {
            this.initProduit();
            this.loadProduits();
          }
        );
      }
     
      updateProduit(){
        this.produitService.updateProduit(this.selectedProduit).subscribe(
          res => {
            this.initProduit();
            this.loadProduits();
          }
        );
      }
     
      initProduit(){
        this.selectedProduit =  new Produit();
        this.createForm();
      }
     
      deleteProduit(){
        this.produitService.deleteProduit(this.selectedProduit.id).subscribe(
          res => {
            this.loadProduits();
          }
        );
      }
     
     
    }


    Code HTML :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    <!-- <h3>Hello produits</h3>
    <ul >
        <li *ngFor="let product of products">
            Ref : {{product.ref}}
            Quantité : {{product.quantite}}
            Prix unitaire : {{product.prixUnitaire}}
        </li>
    </ul> -->
    <h3>Produits</h3>
    <div class="container">
        <div class="row">
            <div class="col-lg-7">
                <table class="table table-hover">
                    <thead>
                        <tr>
                            <th>Référence</th>
                            <th>Quantité</th>
                            <th>Prix Unitaire</th>
                            <th><button class="btn btn-outlet-primary"
                                    (click)="operation = 'add';initProduit();">ADD</button></th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr *ngFor="let produit of products">
                            <td>{{produit.ref}}</td>
                            <td>{{produit.quantite}}</td>
                            <td>{{produit.prixUnitaire}}</td>
                            <td><button class="btn btn-outlet-primary"
                                    (click)="operation = 'edit'; selectedProduit = produit">Edit</button></td>
                            <td><button class="btn btn-outlet-danger" (click)="operation = 'remove'; selectedProduit = produit">Remove</button></td>
                        </tr>
                    </tbody>
                </table>
            </div>
            <div class="col-lg-5">
                <div class="card">
                    <div class="card-header bg-info text-white">
                        {{operation == 'add' ? 'Ajouter produit':
                        operation == 'edit' ? 'Modifier produit':
                        operation == 'remove' ? 'Supprimer produit':''}}
                    </div>
                    <div class="card-body">
                        <div *ngIf="operation == 'add' || operation == 'edit'">
                            <form [formGroup]="produitForm">
                                <div class="form-group">
                                    <label>Réference : </label>
                                    <input type="text" class="form-control" formControlName="ref"
                                        [(ngModel)]="selectedProduit.ref" />
                                </div>
                                <div class="alert alert-danger" *ngIf="produitForm.controls['ref'].invalid && 
                                        (produitForm.controls['ref'].dirty || produitForm.controls['ref'].touched)">
                                    Réference est obligatoire !
                                </div>
                                <div class="form-group">
                                    <label>Quantité : </label>
                                    <input type="number" class="form-control" formControlName="quantite"
                                        [(ngModel)]="selectedProduit.quantite" />
                                </div>
                                <div class="form-group">
                                    <label>Prix unitaire : </label>
                                    <input type="number" class="form-control" formControlName="prixUnitaire"
                                        [(ngModel)]="selectedProduit.prixUnitaire" />
                                </div>
                                <button class="btn btn-success"
                                    (click)="operation == 'add' ? addProduit() : updateProduit()"
                                    [disabled]="(produitForm.prestine || produitForm.invalid)">
                                    {{operation == 'add' ? 'Ajouter' : operation == 'edit' ? 'Modifier' : ''}}</button>
                            </form>
                        </div>
                        <div *ngIf="operation == 'remove'">
                            <p class="card-title">Réference : {{selectedProduit.ref}}</p>
                            <p class="card-title">Voulez-vous supprimer ce produit ?</p>
                            <button class="btn btn-success" [disabled]="!selectedProduit.ref" (click)="deleteProduit()">Confirmez</button>
                        </div>
     
                    </div>
                    <div class="card-footer">
     
                    </div>
                </div>
            </div>
        </div>
    </div>


    Code js :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    import { ProduitComponent } from './produit/produit.component';
    import { DashboardComponent } from './dashboard/dashboard.component';
    import { ProduitResolver } from './produit/produit.resolver';
     
    export const appRoutes : Routes = [
        {
            path:'produit',
            component:ProduitComponent,
            resolve:{
                produits : ProduitResolver    
            }
        },
        {
            path:'dashboard',
            component:DashboardComponent
        },
        {
            path:'',
            redirectTo:'/dashboard',
            pathMatch:'full'
     
        }
    ]
     
    @NgModule({
        imports : [
            RouterModule.forRoot(
                appRoutes,
                {enableTracing:true}
            )
        ],
        exports : [RouterModule],
        providers : [ProduitResolver]
    })
     
    export class AppRoutingModule {
     
    }



    Code js :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { Injectable } from "@angular/core";
    import { Resolve } from "@angular/router";
    import { ProduitService } from "./produit.service";
     
    @Injectable()
    export class ProduitResolver implements Resolve<any> {
     
        constructor(private produitService : ProduitService) {
     
        }
     
        resolve() {
            return this.produitService.getPoduits();
        }
    }



    Code JSON :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    {
      "name": "gestion-stock",
      "version": "0.0.0",
      "scripts": {
        "ng": "ng",
        "start": "ng serve --proxy-config proxy.conf.json",
        "build": "ng build",
        "test": "ng test",
        "lint": "ng lint",
        "e2e": "ng e2e"
      },
      "private": true,
      "dependencies": {
        "@angular/animations": "^6.1.0",
        "@angular/common": "^6.1.0",
        "@angular/compiler": "^6.1.0",
        "@angular/core": "^6.1.0",
        "@angular/forms": "^6.1.0",
        "@angular/http": "^6.1.0",
        "@angular/platform-browser": "^6.1.0",
        "@angular/platform-browser-dynamic": "^6.1.0",
        "@angular/router": "^6.1.0",
        "core-js": "^2.5.4",
        "rxjs": "~6.2.0",
        "zone.js": "~0.8.26",
        "bootstrap": "^4.0.0-beta",
        "font-awesome": "^4.7.0",
        "jquery": "^3.2.1",
        "popper.js": "^1.12.5"
      },
      "devDependencies": {
        "@angular-devkit/build-angular": "~0.8.0",
        "@angular/cli": "~6.2.5",
        "@angular/compiler-cli": "^7.2.5",
        "@angular/language-service": "^6.1.0",
        "@types/jasmine": "~2.8.8",
        "@types/jasminewd2": "~2.0.3",
        "@types/node": "~8.9.4",
        "codelyzer": "~4.3.0",
        "jasmine-core": "~2.99.1",
        "jasmine-spec-reporter": "~4.2.1",
        "karma": "^4.0.0",
        "karma-chrome-launcher": "~2.2.0",
        "karma-coverage-istanbul-reporter": "~2.0.1",
        "karma-jasmine": "~1.1.2",
        "karma-jasmine-html-reporter": "^0.2.2",
        "protractor": "~5.4.0",
        "ts-node": "~7.0.0",
        "tslint": "~5.11.0",
        "typescript": "~3.1.1"
      }
    }


    et côté back end : java spring boot 2

    Code Java :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    package com.gestion.stock.controller;
     
    import java.util.List;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.DeleteMapping;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.PutMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    import com.gestion.stock.entity.Produit;
    import com.gestion.stock.service.IProduitService;
     
    @RestController
    @RequestMapping("/api/produit")
    //@CrossOrigin
    @CrossOrigin(origins = "http://localhost:4200")
    public class ProduitController {
    	@Autowired
    	private IProduitService produitService;
     
    	@GetMapping
    	public List<Produit> getProduits() {
    		return produitService.getProduits();
    	}
     
    	@PostMapping
    	public void addProduit(@RequestBody Produit produit) {
    		produitService.addProduit(produit);
    	}
     
    	@PutMapping
    	public void updateProduit(@RequestBody Produit produit) {
    		produitService.updateProduit(produit);
    	}
     
    	@DeleteMapping("{/id}")
    	public void deletedProduit(@PathVariable Long id) {
    		produitService.deleteProduit(id);
    	}
    }



    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    spring.jpa.hibernate.ddl-auto=create
    spring.datasource.url=jdbc:mysql://localhost:3306/stock_produits?zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=UTC
    spring.datasource.username=root
    spring.datasource.password=root


    je n'arrive pas à trouver la solution après multiple recherche sur le net.

  2. #2
    Membre expert
    Le service appelé ne fournit pas d'header "Access-Control-Allow-Origin".

    C'est un header servant à gérer la liste des services autorisés par la page web (le serveur indique que la page est autorisée à invoquer des services se trouvant dans le domaine indiqué).

    Dans ton cas, certainement que le port n'est pas identique entre l'url de la page web et l'url du service appelé. Ce qui pose des soucis de sécurité (CORS -> Cross Origin Resource Sharing)

    Cf. https://developer.mozilla.org/en-US/...l-Allow-Origin
    "Le plug gros problème des citations trouvées sur internet, c'est qu'on ne peut jamais garantir leur authenticité"

    Confucius, 448 av. J-C

  3. #3
    Membre du Club
    Bonjour et merci.

    ETant dans la configuration spring boot avec un repertoire pour mes entités, un repertoire pour mes services, un repertoire pour mes repository et un repertoir pour mes controleurs.

    Ma question est de savoir comment l'intégrer dans mon contrôleur via l'annotation @CrossOrigin

    Exemple :
    Code Java :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    package com.gestion.stock.controller;
     
    import java.util.List;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.DeleteMapping;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.PutMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    import com.gestion.stock.entity.Produit;
    import com.gestion.stock.service.IProduitService;
     
    @RestController
    //@RequestMapping("/api/produit")
    //@CrossOrigin
    //@CrossOrigin(origins = "http://localhost:4200")
    @RequestMapping("/api")
    //@CrossOrigin("*")
    @CrossOrigin(origins = "http://localhost:4200", methods='POST, GET, PUT, OPTIONS, DELETE')
    public class ProduitController {
    	@Autowired
    	private IProduitService produitService;
     
    	// @GetMapping
    	@GetMapping("/produits")
    	public List<Produit> getProduits() {
    		return produitService.getProduits();
    	}
     
    	// @PostMapping
    	@PostMapping("/produit")
    	public void addProduit(@RequestBody Produit produit) {
    		produitService.addProduit(produit);
    	}
     
    	// @PutMapping
    	@PutMapping("/produit")
    	public void updateProduit(@RequestBody Produit produit) {
    		produitService.updateProduit(produit);
    	}
     
    	// @DeleteMapping("{/id}")
    	@DeleteMapping("/produit/{id}")
    	public void deletedProduit(@PathVariable("id") Long id) {
    		produitService.deleteProduit(id);
    	}
    }


    Dans la documentation spring, on en parle ici, mais je ne vois pas comment l'intégrer

    This @CrossOrigin annotation enables cross-origin requests only for this specific method. By default, its allows all origins, all headers, the HTTP methods specified in the @RequestMapping annotation and a maxAge of 30 minutes is used. You can customize this behavior by specifying the value of one of the annotation attributes: origins, methods, allowedHeaders, exposedHeaders, allowCredentials or maxAge.
    Quelque chose du style : @CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)

  4. #4
    Expert éminent sénior
    Il faudrait déjà voir un peu plus ce qui se passe au niveau de ton browser lors du pre-flight CORS (donc regarder ce qui s'échange dans l'onglet network du debugger). Un truc que j'ai vu régulièrement passer avec le CORS, c'est qu'il soit appliqué sur un controlleur nécessitant de l'authentification pour tout. Vu que le browser envoie la requête pre-flight sans rien de sensible, en général elle ne passe pas le filtre d'authentification et on reçois un 403 ou un 401 ce que le browser interprête comme "pas de CORS".
    David Delbecq Java developer chez HMS Industrial Networks AB. &#12288;&#12288;&#12288;LinkedIn | Google+

  5. #5
    Membre expert
    Et aussi, je ne mettrais pas cela sous forme d'annotations dans le code. Il y a fort à parier que cette config ne soit valable qu'en mode dev et doive être désactivée lors des déploiement réels (typique de l'utilisation de nodejs - le backend est exposé sur un port différent du front).

    Donc, plutôt configurer un filter avec de l'xml qu'une annotation Java (filter qui sera supprimé de la config finale): https://spring.io/blog/2015/06/08/co...ring-framework

    M'enfin c'est personnel
    "Le plug gros problème des citations trouvées sur internet, c'est qu'on ne peut jamais garantir leur authenticité"

    Confucius, 448 av. J-C