skip to Main Content

I am doing a react app with spring boot for the back end and mysql as the database. Its a simple CRUD of notes, but I am having a problem when fetching from the API to execute the methods. When I send a request through postman i have no errors, and I can see the note added in the database, but when going through the web app, I get an error. I have very little idea how to fetch data from an api with react, I was just following a tutorial and modifying a bit for my project. I know there are also things like await but don’t know if the problem is that I didn’t use that. I get the following error

Access to fetch at 'http://localhost:8080/api/note' from origin 'http://localhost:3000' 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. If an opaque response serves your needs, set the 
request's mode to 'no-cors' to fetch the resource with CORS disabled.

POST http://localhost:8080/api/note net::ERR_FAILED

Uncaught (in promise) TypeError: Failed to fetch

Here is my code.

Controller class

@RestController
@RequestMapping(path = "api/note")
@CrossOrigin(origins = "*")
public class NoteController {

    @Autowired
    NoteService noteService;

    @PostMapping(path = "/")
    public ResponseEntity<NoteModel> createNote(@RequestBody NoteModel newNote) {
        return new ResponseEntity<>(this.noteService.createNote(newNote), HttpStatus.OK);
    }
    @PutMapping(path = "/{id}")
    public ResponseEntity<NoteModel> updateNote(@RequestBody NoteModel note, @PathVariable Long id) throws Exception {
        return new ResponseEntity<>(this.noteService.updateNote(note, id), HttpStatus.OK);
    }
    @GetMapping(path = "/")
    public ResponseEntity<List<NoteModel>> findAllNotes(){
        return new ResponseEntity<>(this.noteService.findAllNotes(), HttpStatus.OK);
    }
    @DeleteMapping(path = "/{id}")
    public ResponseEntity<String> createNote(@PathVariable Long id) throws Exception {
        this.noteService.deleteNote(id);
        return new ResponseEntity<>("Note deleted successfully", HttpStatus.CREATED);
    }
}

React component doing the fetching.

import React from 'react'
import closeButton from '../images/close-button.png'
import { useState } from 'react';

function Popup(props) {
  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  const handleClick=(e) => {
    e.preventDefault()
    const note = {title, content}
    console.log(title, content)
    fetch("http://localhost:8080/api/note", {
      method: "POST",
      headers: {"Content-Type": "application/json"},
      body: JSON.stringify(note)
    }).then(()=>{
      console.log("New note added!")
    })

    setTitle("")
    setContent("")
    props.setTrigger(false)
  }

  const handleExit=(e) => {
    e.preventDefault()
    props.setTrigger(false)
    setTitle("")
    setContent("")
  }

  return (props.trigger) ? (
    <div className="popup">
        <div className="inner-popup">
          <img src={closeButton} className="close-btn" onClick={handleExit} alt="Close"/>
          <h2 className="popup-title">Add new note</h2>
          <form>
            <div className="form-title">
                <label>Title</label>
                <input 
                    type="text"
                    id="form-title-input"
                    name="form-title-input"
                    placeholder="Note Title"
                    value={title}
                    onChange={(e)=>setTitle(e.target.value)}
                />
            </div>
            <div className="form-content">
                <label>Content</label>
                <textarea 
                    id="note-content-txtf"
                    name="note-content"
                    placeholder="Note about something..."
                    value={content}
                    onChange={(e)=>setContent(e.target.value)}
                />
            </div>
            <div className="form-buttons">
                <button className="btn box-shw btn-cancel" onClick={handleExit}>Cancel</button>
                <button className="btn box-shw btn-confirm" onClick={handleClick}>Confirm</button>
            </div>
          </form>
        </div>
    </div>
  ) : "";
}

export default Popup

I tried adding @CrossOrigin(origins="*") to the controller class in java, still didnt work. Disabling cors with mode: "no-cors" in the react app when fetching made the error not appear, but it still didnt POST. Tried adding CorsConfiguration class in java still no luck. Also tried adding Access-Control-Allow-Origin: "*" to the headers in the fetch on the react app, but still doesn’t work. I have no idea what else I can try so if anybody can give me a hand I would appreciate it.

2

Answers


  1. You can try with some changes in your current implementation as shown below:

    1. @CrossOrigin at the Class Level with maxAge.

      since maxAge = 3600, all pre-flight responses will be cached for 60
      mins.:

      @CrossOrigin(maxAge = 3600)
      @RestController
      @RequestMapping(path = "api/note")
      public class NoteController {
      
    2. Enabling CORS Globally

      Instead of adding CORS to each of the resources separately, we can define a common CORS configuration that would apply to all defined resources.You can use a WebMvcConfigurer,by overriding the addCorsMapping() method you can configure CORS to all URLs.You can create a @Bean and include this code in your main class.

          @Bean
          public WebMvcConfigurer corsConfigurer() {
          return new WebMvcConfigurer() {
              @Override
              public void addCorsMappings(CorsRegistry registry) {
                  registry.addMapping("/**")
                          .allowedOrigins("http://localhost:3000")
                          .allowedMethods("GET", "POST", "PUT", "DELETE");
              }
          };
      }
      
    Login or Signup to reply.
  2. If you created the React project using CRA, then add the following to package.json:

    "proxy": "http://localhost:8080/"
    

    If you are using vite then you can add the following to vite.config.js

    server: {
      proxy: {
        "/api": {
          target: "http://localhost:8080/",
          secure: false
        }
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search