My ReactJS/Django(Rest API) prompts an Unhandled Promise rejection when I browse from a different PC on the LAN

307 Views Asked by At

I can see the frontend form though, but if I try to submit the console would show: "Fetch API cannot load http://localhost:8000/api/task-create/ due to access control checks." and the next line is: "Failed to load resource: Cannot establish connection with the server."

This is what I see on my PC, (no problem with this every function works perfectly) What I see on my Laptop

And this is what I get on a different PC connected to the same LAN What I see on a different PC

I think it has something to do with headers and CORS, Django REST Framework Authentication, I have tried it all but it doesn't seem to work.

Any hints?

This is my settings.py:

Django settings for todoapp project.

Generated by 'django-admin startproject' using Django 3.2.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""

from pathlib import Path
import os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-whh5g$8**@u4t1m%13=c$6!k-5#o0jwhr&&7i7^d$ky%$ku+ls'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'corsheaders',
    'api.apps.ApiConfig'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'corsheaders.middleware.CorsMiddleware',
]

ROOT_URLCONF = 'todoapp.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'frontend/build')
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'todoapp.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'frontend/build/static')
]

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

CORS_ALLOWED_ORIGINS = [
    "http://192.168.0.6:3000",
]

my views.py:

from django.shortcuts import render
from django.http import JsonResponse

from rest_framework.decorators import api_view
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .serializers import TaskSerializer

from .models import Task

# Create your views here.
@api_view(['GET'])
def apiOverview(request):
    api_urls = {
        'List':'task-list/',
        'Detail View':'/task-detail/<str:pk>',
        'Create':'/task-create/',
        'Update':'task-update/<str:pk>',
        'Delete':'task-delete/<str:pk>', 
    }
    return Response(api_urls)

@api_view(['GET'])
def taskList(request):
    tasks = Task.objects.all()
    serializer = TaskSerializer(tasks, many=True)
    return Response(serializer.data)

@api_view(['GET'])
def taskDetail(request, id):
    task = Task.objects.get(id=id)
    serializer = TaskSerializer(task, many=False)
    return Response(serializer.data)

@api_view(['POST'])
def taskCreate(request):
    serializer = TaskSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
    return Response(serializer.data)    

@api_view(['POST'])
def taskUpdate(request, id):
    task = Task.objects.get(id=id)
    serializer = TaskSerializer(instance=task, data=request.data)
    if serializer.is_valid():
        serializer.save()
    return Response(serializer.data)

@api_view(['DELETE'])
def taskDelete(request, id):
    task = Task.objects.get(id=id)
    task.delete()
    return Response('Task succesfully deleted!')

and my react main component App.js:

import React from "react"
import './App.css';
import TodoItem from "./TodoItem"
import Form from "./Form"


class App extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      todoList:[],
      activeItem:{
        id:null,
        title:'',
        completed:false,
      },
      editing:false,
    }
    this.fetchTasks = this.fetchTasks.bind(this)
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
    this.getCookie = this.getCookie.bind(this)
    this.handleEdit = this.handleEdit.bind(this)
    this.handleDelete = this.handleDelete.bind(this)
    this.handleCompleted = this.handleCompleted.bind(this)
  }

  getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
  }

  componentDidMount(){
    this.fetchTasks()
  }

  fetchTasks(){
    fetch('http://localhost:8000/api/task-list/')
    .then(response => response.json())
    .then(data =>
            this.setState({
              todoList: data
            })
      )
  }

  handleChange(event){
    let value = event.target.value
    this.setState({
      activeItem: {
        ...this.state.activeItem,
        title: value
      }
    })
  }

  handleSubmit(event){
      event.preventDefault()
      let csrftoken = this.getCookie('csrftoken') 
      let url='http://localhost:8000/api/task-create/'
      if(this.state.editing === true){
        url=`http://localhost:8000/api/task-update/${this.state.activeItem.id}/`  
        this.setState({
          editing: false
        })
      } 
      fetch(url,{
      method: 'POST',
      headers:{
        'Content-type':'application/json',
        'X-CSRFToken': csrftoken,
        },
      body: JSON.stringify(this.state.activeItem)
      }).then(()=>{
        this.fetchTasks()
        this.setState({
          activeItem:{
            id:null,
            title:'',
            completed:false,
          }
        })
      }).catch((e)=>{
        console.log('ERROR:',e)
      })
  }

  handleEdit(todo){
    this.setState({
      activeItem: todo,
      editing: true,
    })
  }

  handleDelete(todo){
    let csrftoken = this.getCookie('csrftoken')
    let url = `http://localhost:8000/api/task-delete/${todo.id}/`
    fetch(url, {
      method: 'DELETE',
      headers: {
        'Content-type': 'application/json',
        'X-CSRFToken': csrftoken
      }
    }).then((response)=>{
      this.fetchTasks()
    }).catch((error)=>{
      console.log('ERROR:',error)
    })
  }

  handleCompleted(todo){
    let csrftoken = this.getCookie('csrftoken')
    const url=`http://localhost:8000/api/task-update/${todo.id}/`
    todo.completed= !todo.completed
    fetch(url,{
      method:'POST',
      headers:{
        'Content-type': 'application/json',
        'X-CSRFToken': csrftoken
      },
      body: JSON.stringify({
        title:todo.title,
        completed:todo.completed,})
    }).then(()=>{
      this.fetchTasks()
    }).catch((e)=>{
      console.log('ERROR:',e)
    })
  }

  render(){
    let todoItems = this.state.todoList.map((todo, id)=>{ 
      return( 
        <TodoItem 
          key={id} todo={todo} 
          onClick={()=> this.handleEdit(todo)} 
          onDelete={()=> this.handleDelete(todo)}
          onCompleted={()=> this.handleCompleted(todo)}
        />
      ) 
    })
    return(
      <div className="container">
          <div id="task-container">
              <Form 
                onChange={this.handleChange} 
                onSubmit={this.handleSubmit} 
                data={this.state.activeItem}
              />
              <div id="list-wrapper">
                {todoItems} 
              </div>  
          </div>
      </div>
    )
  }    
}

export default App;

Thanks for the help in adavnce...

1

There are 1 best solutions below

0
Elias Prado On

You might having issue with the ip address. Like, this different computer might have an activated VPN or any other issue that points to other ip adress not a localhost.

So, in your django settings.py try to change this:

CORS_ALLOWED_ORIGINS = [
    "http://192.168.0.6:3000",
]

to this approach:

CORS_ORIGIN_WHITELIST = [
     'http://localhost:3000',
]

Or if you want to allow all:

CORS_ORIGIN_ALLOW_ALL = True # If you use this approach then `CORS_ORIGIN_WHITELIST` will not have any effect
CORS_ALLOW_CREDENTIALS = True

And check if it works.