How to fix this: Resolved [org.springframework.web.HttpRequestMethodNotSupportedException:...

Discussão em 'Angular' iniciado por TheRealKing, Fevereiro 20, 2025.

  1. TheRealKing

    TheRealKing Guest

    I have a Spring Boot REST API, and it works perfectly when tested via Postman—all HTTP methods function as expected. However, when I try to interact with the API from the client-side application, I encounter errors on both the backend and the frontend.

    Backend Error Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported]

    Frontend Error [![enter image description here][1]][1]


    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;

    import java.io.IOException;
    import java.util.List;
    import java.util.Optional;

    @RestController
    @RequestMapping("/api/v1/persons")
    @CrossOrigin(origins = "http://localhost:4200")
    public class PersonController {

    @Autowired
    private PersonRepository personRepository;

    public PersonController(PersonRepository personRepository) {
    this.personRepository = personRepository;
    }

    @RequestMapping(
    value = "/api/v1/persons",
    produces = "application/json",
    consumes = { MediaType.MULTIPART_FORM_DATA_VALUE },
    method = RequestMethod.GET)
    public ResponseEntity<Person> createPerson(
    @RequestParam String firstname,
    @RequestParam String lastname,
    @RequestParam String email,
    @RequestParam int age,
    @RequestPart(value = "photo", required = false) MultipartFile photo,
    @RequestPart(value = "cv", required = false) MultipartFile cv){

    try {
    Person person = new Person();
    person.setFirstname(firstname);
    person.setLastname(lastname);
    person.setEmail(email);
    person.setAge(age);

    if (photo != null && !photo.isEmpty()) {
    person.setPhoto(photo.getBytes());
    }

    if (cv != null && !cv.isEmpty()) {
    person.setCv(cv.getBytes());
    }
    System.out.println("Save and upload of the files successfully done");
    return ResponseEntity.ok(personRepository.save(person));
    } catch (IOException e) {
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
    }

    // Get All Persons
    @GetMapping
    public List<Person> getAllPersons() {
    return personRepository.findAll();
    }

    // Get Person by ID
    @GetMapping("/{id}")
    public ResponseEntity<Person> getPersonById(@PathVariable Long id) {
    Optional<Person> person = personRepository.findById(id);
    return person.map(ResponseEntity::eek:k).orElseGet(() -> ResponseEntity.notFound().build());
    }

    // Update Person
    @PutMapping("/{id}")
    public ResponseEntity<Person> updatePerson(
    @PathVariable Long id,
    @RequestParam String firstname,
    @RequestParam String lastname,
    @RequestParam String email,
    @RequestParam int age,
    @RequestParam(value = "photo", required = false) MultipartFile photo,
    @RequestParam(value = "cv", required = false) MultipartFile cv) {

    Optional<Person> existingPerson = personRepository.findById(id);

    if (existingPerson.isPresent()) {
    Person person = existingPerson.get();
    person.setFirstname(firstname);
    person.setLastname(lastname);
    person.setEmail(email);
    person.setAge(age);

    try {
    if (photo != null && !photo.isEmpty()) {
    person.setPhoto(photo.getBytes());
    }
    if (cv != null && !cv.isEmpty()) {
    person.setCv(cv.getBytes());
    }
    } catch (IOException e) {
    System.out.println("There is something going waring: " + e.getMessage());
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }

    return ResponseEntity.ok(personRepository.save(person));
    } else {
    return ResponseEntity.notFound().build();
    }
    }

    // Download Person's Photo
    @GetMapping("/{id}/photo")
    public ResponseEntity<byte[]> getPersonPhoto(@PathVariable Long id) {
    Optional<Person> person = personRepository.findById(id);

    if (person.isPresent() && person.get().getPhoto() != null) {
    return ResponseEntity.ok()
    .header(HttpHeaders.CONTENT_TYPE, "image/png")
    .body(person.get().getPhoto());
    }
    return ResponseEntity.notFound().build();
    }

    // Download Person's CV
    @GetMapping("/{id}/cv")
    public ResponseEntity<byte[]> getPersonCv(@PathVariable Long id) {
    Optional<Person> person = personRepository.findById(id);

    if (person.isPresent() && person.get().getCv() != null) {
    return ResponseEntity.ok()
    .header(HttpHeaders.CONTENT_TYPE, "application/pdf")
    .body(person.get().getCv());
    }
    return ResponseEntity.notFound().build();
    }

    // Delete Person
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deletePerson(@PathVariable Long id) {
    if (personRepository.existsById(id)) {
    personRepository.deleteById(id);
    return ResponseEntity.noContent().build();
    } else {
    return ResponseEntity.notFound().build();
    }
    }
    }


    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.CorsConfigurationSource;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

    import java.util.List;

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
    .csrf(csrf -> csrf.disable()) // Disable CSRF
    .cors(cors -> cors.configurationSource(corsConfigurationSource())) // Enable CORS
    .authorizeHttpRequests(auth -> auth
    .requestMatchers(HttpMethod.POST,"/api/v1/persons/**").permitAll() // Allow all requests to this path
    .anyRequest().permitAll() // Allow all requests globally (disable authentication)
    );

    return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(List.of("http://localhost:4200"));
    configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    configuration.setAllowedHeaders(List.of("*"));
    configuration.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
    }


    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.4.2</version>
    <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>nl.alexandarabski</groupId>
    <artifactId>person-details</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>person-details</name>
    <description>Person details project for Spring Boot</description>
    <url/>
    <licenses>
    <license/>
    </licenses>
    <developers>
    <developer/>
    </developers>
    <scm>
    <connection/>
    <developerConnection/>
    <tag/>
    <url/>
    </scm>
    <properties>
    <java.version>17</java.version>
    </properties>
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>

    </project>


    server:
    port: 8081

    spring:
    mvc:
    hiddenmethod:
    filter:
    enabled: true
    datasource:
    url: jdbc:postgresql://localhost:5333/personsdetails
    hikari:
    username: sanny
    password: sanny13
    jpa:
    hibernate:
    ddl-auto: update
    properties:
    hibernate:
    dialect: org.hibernate.dialect.PostgreSQLDialect
    format_sql: true
    show-sql: true
    servlet:
    multipart:
    enabled: true
    max-file-size: 5MB
    max-request-size: 10MB
    main:
    web-application-type: servlet


    export interface Person {
    id?: number;
    firstname: string;
    lastname: string;
    email: string;
    age: number;
    photo: File | null;
    cv: File | null;
    }


    import { HttpClient, HttpHeaders } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    import { Observable } from 'rxjs';
    import { map } from 'rxjs/operators';
    import { Person } from './person';

    @Injectable({
    providedIn: 'root'
    })
    export class PersonService {
    private baseUrl: string = "http://localhost:8081/api/v1/persons";
    //headers: HttpHeaders | { [header: string]: string | string[]; } | undefined;

    constructor(private _httpClient: HttpClient) { }

    fetchAllPersons(): Observable<Person[]> {
    return this._httpClient.get<Person[]>(this.baseUrl).pipe(
    map((persons: Person[]) => persons.map(person => ({
    ...person,
    photo: person.photo ? `data:image/png;base64,${person.photo}` : null,
    cv: person.cv ? `data:application/pdf;base64,${person.cv}` : null
    })))
    );
    }

    createPerson(personData: FormData): Observable<Person> {
    return this._httpClient.post<Person>(`${this.baseUrl}`, personData);
    }

    updatePerson(personId: number, personData: FormData): Observable<Person> {
    return this._httpClient.put<Person>(`${this.baseUrl} ${personId}`, personData);
    }

    deletePerson(id: number): Observable<void> {
    return this._httpClient.delete<void>(`${this.baseUrl}/${id}`);
    }
    }


    import { ChangeDetectionStrategy, Component,NgModule, inject } from '@angular/core';
    import { MatButtonModule } from '@angular/material/button';
    import { MAT_DIALOG_DATA, MatDialogActions, MatDialogContent, MatDialogModule, MatDialogRef, MatDialogTitle } from '@angular/material/dialog';
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { MatInputModule } from '@angular/material/input';
    import { MatIconModule } from '@angular/material/icon';
    import { Person } from '../person';
    import { PersonService } from '../person.service';
    import { CommonModule } from '@angular/common';

    import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';

    @Component({
    selector: 'app-person-form',
    imports: [
    MatDialogModule,
    MatDialogContent,
    MatDialogActions,
    MatButtonModule,
    MatFormFieldModule,
    MatIconModule,
    MatInputModule,
    CommonModule,
    ReactiveFormsModule,
    FormsModule
    ],
    changeDetection: ChangeDetectionStrategy.OnPush,
    templateUrl: './person-form.component.html',
    styleUrl: './person-form.component.css'
    })
    export class PersonFormComponent {
    personForm: FormGroup;
    selectedPhoto: File | null = null;
    selectedCv: File | null = null;

    readonly dialogRef = inject(MatDialogRef<PersonFormComponent>);
    data = inject<Person>(MAT_DIALOG_DATA);

    constructor(private personService: PersonService, private fb: FormBuilder) {
    this.personForm = this.fb.group({
    id: [this.data?.id || null],
    firstname: [this.data?.firstname || '', Validators.required],
    lastname: [this.data?.lastname || '', Validators.required],
    email: [this.data?.email || '', [Validators.required, Validators.email]],
    age: [this.data?.age || null, [Validators.required, Validators.min(1)]]
    });
    }

    onFileSelected(event: any, fileType: string) {
    const file = event.target.files[0];
    if (file) {
    if (fileType === 'photo') {
    this.selectedPhoto = file;
    } else if (fileType === 'cv') {
    this.selectedCv = file;
    }
    }
    }

    addOrUpdatePerson() {
    const formData = new FormData();
    formData.append('firstname', this.personForm.value.firstname);
    formData.append('lastname', this.personForm.value.lastname);
    formData.append('email', this.personForm.value.email);
    formData.append('age', String(this.personForm.value.age)); // Convert age to string

    if (this.selectedPhoto) {
    formData.append('photo', this.selectedPhoto);
    }

    if (this.selectedCv) {
    formData.append('cv', this.selectedCv);
    }

    if (this.personForm.value.id) {
    // Update existing person (PUT)
    this.personService.updatePerson(this.personForm.value.id, formData).subscribe({
    next: (response) => {
    console.log('Person updated successfully', response);
    this.dialogRef.close(response); // Close the dialog with updated data
    },
    error: (error) => console.error('Error updating person:', error)
    });
    } else {
    // Create new person (POST)
    this.personService.createPerson(formData).subscribe({
    next: (response) => {
    console.log('Person created successfully', response);
    this.dialogRef.close(response); // Close the dialog with new data
    },
    error: (error) => console.error('Error creating person:', error)
    });
    }
    }
    }


    import { Component, ViewChild, AfterViewInit, inject } from '@angular/core';
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { MatInputModule } from '@angular/material/input';
    import { MatButtonModule } from '@angular/material/button';
    import { MatTableDataSource, MatTableModule } from '@angular/material/table';
    import { MatCardModule } from '@angular/material/card';
    import { Person } from '../person';
    import { PersonService } from '../person.service';
    import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
    import { CommonModule } from '@angular/common';
    import { MatSort, MatSortModule } from '@angular/material/sort';
    import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
    import { MatDialog, MatDialogModule } from '@angular/material/dialog';
    import { PersonFormComponent } from '../person-form/person-form.component';


    @Component({
    selector: 'app-home',
    imports: [
    MatFormFieldModule, MatInputModule, MatButtonModule, MatTableModule,
    MatCardModule, CommonModule, MatSortModule, MatPaginatorModule, MatDialogModule
    ],
    templateUrl: './home.component.html',
    styleUrl: './home.component.css'
    })
    export class HomeComponent implements AfterViewInit {
    person: Person = { // ✅ Add this property
    id: 0,
    firstname: '',
    lastname: '',
    email: '',
    age: 0,
    photo: null,
    cv: null
    };

    displayedColumns: string[] = ['id', 'firstname', 'lastname', 'email', 'age', 'photo', 'cv', 'edit', 'delete'];
    dataSource = new MatTableDataSource<Person>();
    persons: Person[] = [];
    filteredPersons: Person[] = [];
    readonly dialog = inject(MatDialog);

    @ViewChild(MatSort) sort!: MatSort;
    @ViewChild(MatPaginator) paginator!: MatPaginator;

    constructor(private personService: PersonService, private sanitizer: DomSanitizer) {}

    ngAfterViewInit(): void {
    this.loadPersons();
    }

    loadPersons(): void {
    this.personService.fetchAllPersons().subscribe((data) => {
    this.persons = data;
    this.dataSource = new MatTableDataSource<Person>(this.persons);
    this.dataSource.sort = this.sort;
    this.dataSource.paginator = this.paginator;
    });
    }

    searchPerson(input: string): void {
    this.filteredPersons = this.persons.filter(item =>
    item.firstname.toLowerCase().includes(input.toLowerCase()) ||
    item.lastname.toLowerCase().includes(input.toLowerCase()) ||
    item.email.toLowerCase().includes(input.toLowerCase()) ||
    item.age.toString().includes(input)
    );
    this.dataSource = new MatTableDataSource<Person>(this.filteredPersons);
    }

    openDialog(person?: Person): void {
    const dialogRef = this.dialog.open(PersonFormComponent, {
    data: person
    });

    dialogRef.afterClosed().subscribe(result => {
    if (result) {
    this.loadPersons();
    }
    });
    }

    deletePerson(id: number): void {
    const isConfirmed = window.confirm('Are you sure you want to delete?');
    if (isConfirmed) {
    this.personService.deletePerson(id).subscribe(() => {
    this.persons = this.persons.filter(item => item.id !== id);
    this.dataSource.data = this.persons;
    });
    }
    }
    }


    <div class="container" style="width: 450px;">
    <div>
    <button mat-icon-button style="float: right; margin: 15px;">
    <mat-icon style="color:red;">Close</mat-icon>
    </button>
    </div>

    <div mat-dialog-title>
    <h3 style="color: blueviolet; text-align: center;">Person Form</h3>
    </div>


    <div mat-dialog-content>
    <mat-form-field>
    <input matInput [(ngModel)]="data.firstname" type="text" placeholder="Enter first name">
    <mat-icon fontIcon="person" matSuffix></mat-icon>
    </mat-form-field>
    <br>
    <mat-form-field>
    <input matInput [(ngModel)]="data.lastname" type="text" placeholder="Enter last name">
    <mat-icon fontIcon="person" matSuffix></mat-icon>
    </mat-form-field>
    <br>
    <mat-form-field>
    <input matInput [(ngModel)]="data.email" type="text" placeholder="Enter email">
    <mat-icon fontIcon="email" matSuffix></mat-icon>
    </mat-form-field>
    <br>
    <mat-form-field>
    <input matInput [(ngModel)]="data.age" type="number" placeholder="Enter age">
    <mat-icon fontIcon="calendar_today" matSuffix></mat-icon>
    </mat-form-field>
    <br>

    <!-- FILE UPLOAD (OUTSIDE mat-form-field) -->
    <div>
    <label>Upload Photo</label>
    <!-- <input type="file" (change)="onFileSelected($event, 'photo')" accept="image/*"> -->
    <input type="file" (change)="onFileSelected($event, 'photo')" />
    </div>
    <br>
    <div>
    <label>Upload CV</label>
    <!-- <input type="file" (change)="onFileSelected($event, 'cv')" accept=".pdf"> -->
    <input type="file" (change)="onFileSelected($event, 'cv')" />
    </div>

    </div>


    <div mat-dialog-actions align="center">
    <button (click)="addOrUpdatePerson()" mat-flat-button [mat-dialog-close]="data">Submit</button>
    <button mat-flat-button mat-dialog-close="true" color="warn">Cancel</button>
    </div>
    </div>


    <div class="container" style="margin-top: 20px; margin-left: 30px; width: 821px">
    <br>
    <h3 style="color:blueviolet; text-align: center;"><strong>Full Stack wiht Angular 19, SpringBoot and Docker</strong></h3>
    <br>
    <mat-form-field>
    <input (input)="searchPerson(filter.value)" matInput placeholder="Search person here" type="text" #filter>
    </mat-form-field>
    <button (click)="openDialog(person)" mat-flat-button style="width: 100px; float: right; margin: 15px">Add +</button>
    <br>
    <mat-table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>

    <!-- Id Column -->
    <ng-container matColumnDef="id">
    <mat-header-cell *matHeaderCellDef mat-sort-header> Id </mat-header-cell>
    <mat-cell *matCellDef="let person"> {{person.id}} </mat-cell>
    </ng-container>

    <!-- Firs name Name Column -->
    <ng-container matColumnDef="firstname">
    <mat-header-cell *matHeaderCellDef mat-sort-header> First Name </mat-header-cell>
    <mat-cell *matCellDef="let person"> {{person.firstname}} </mat-cell>
    </ng-container>

    <!-- Last name Column -->
    <ng-container matColumnDef="lastname">
    <mat-header-cell *matHeaderCellDef mat-sort-header> Last Name </mat-header-cell>
    <mat-cell *matCellDef="let person"> {{person.lastname}} </mat-cell>
    </ng-container>

    <!-- Email Column -->
    <ng-container matColumnDef="email">
    <mat-header-cell *matHeaderCellDef mat-sort-header> Email </mat-header-cell>
    <mat-cell *matCellDef="let person"> {{person.email}} </mat-cell>
    </ng-container>

    <!-- Age Column -->
    <ng-container matColumnDef="age">
    <mat-header-cell *matHeaderCellDef mat-sort-header> Age </mat-header-cell>
    <mat-cell *matCellDef="let person"> {{person.age}} </mat-cell>
    </ng-container>

    <!-- Photo Column -->
    <ng-container matColumnDef="photo">
    <mat-header-cell *matHeaderCellDef mat-sort-header> Photo </mat-header-cell>
    <mat-cell *matCellDef="let person">
    <img *ngIf="person.photo" [src]="person.photo" alt="Person Photo" width="50" height="50"/>
    </mat-cell>

    </ng-container>

    <!-- CV Column -->
    <ng-container matColumnDef="cv">
    <mat-header-cell *matHeaderCellDef mat-sort-header> CV </mat-header-cell>
    <mat-cell *matCellDef="let person">
    <a *ngIf="person.cv" [href]="person.cv" download="CV.pdf">
    <button mat-flat-button color="primary">Download CV</button>
    </a>
    </mat-cell>

    </ng-container>

    <ng-container matColumnDef="edit">
    <mat-header-cell *matHeaderCellDef> Edit </mat-header-cell >
    <mat-cell *matCellDef="let person"> <button mat-flat-button (click)="openDialog(person)" style="height: 27px; width: auto;">Edit</button> </mat-cell >
    </ng-container>

    <ng-container matColumnDef="delete">
    <mat-header-cell *matHeaderCellDef> Delete </mat-header-cell >
    <mat-cell *matCellDef="let person"> <button mat-flat-button (click)="deletePerson(person.id)" style="height: 27px; width: auto;" color="warn">Delete</button> </mat-cell >
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let persons; columns: displayedColumns;"></mat-row>
    </mat-table>
    <mat-paginator [pageSizeOptions]="[5, 10, 20]"
    showFirstLastButtons
    aria-label="Select page of periodic elements">
    </mat-paginator>

    </div>

    Continue reading...

Compartilhe esta Página