Django in Docker on Nginx request to self fails when in the context of another request - django

I have a Django application deployed inside a container on top of Nginx using Dokku. One of the view functions of the Django application includes a request:
views.py
def foo(request):
...
response = requests.get(url)
...
It is probably noteworthy that url is the url of the Django application itself, so the request is from the application to itself. The request is to one of the API endpoints (the reasons for doing this are historical). When the view is called then the request to url fails with 504 gateway timeout.
I cannot reproduce this in any other context specifically:
There is no error when running on localhost with the development server, where url is then the url of the development app (localhost to itself works).
There is no error when running on localhost with the development server, where I manually make the url the production url (localhost to production works).
There is no error when running this request on the production server but outside of the view. Specifically, I did a docker exec into the container, started the Django environment (manage.py shell), and ran the exact request that the view was making, and it worked! (production to production works)
It seems that only when the request is made in the context of a view which itself is answering another request I get an issue.
Any ideas?

Related

How to connect Expo GO App to Django Rest Framework Backend on localhost?

I am implementing a react native application using Expo and testing it on my iOS device using Expo Go. I have a Django rest framework backend running on my local machine that I can access using my browser via http://localhost:8000 - using localhost in my react native app does not work during my fetch request. For instance:
let response = await fetch(BACKEND_URL + "/shft/auth/token/obtain/", {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
});
returns
Network request failed
at node_modules\whatwg-fetch\dist\fetch.umd.js:null in setTimeout$argument_0
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:null in _allocateCallback$argument_0
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:null in _callTimer
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:null in callTimers
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:null in __callFunction
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:null in __guard$argument_0
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:null in __guard
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:null in callFunctionReturnFlushedQueue
I have tried setting BACKEND_URL to localhost:8000 and my public IP via expo-constants
import Constants from "expo-constants";
const { manifest } = Constants;
const uri = `http://${manifest.debuggerHost.split(":").shift()}:8000`;
But neither seems to work. I have enabled the Corsheaders middleware in my DRF project and placed the #csrf_exempt decorator on my APIView's dispatch method, and this error persists. I also added localhost:19000 to my CORS whitelist, which is where Expo seems to host its local server. What could be the problem here? Both the Expo server and the Django server are running locally on my laptop, and otherwise the API works in Django tests.
Using curl on my API endpoints via localhost also works, though the external IP returned by expo constants does not—but this may be because I am sending from localhost.
I found a fix using information from these two posts: this stack overflow and this blog. Neither of these solutions worked for me out of the box but a little networking logic makes it all come together. The core issue here is that the Django server is hosted locally but the Expo Go App is running on a separate mobile device (despite the expo server being hosted locally as well). I've tested each of the following steps to make sure they are all necessary.
Set up Django CORS using the corsheaders app and the corresponding middleware, there are guides on how to do this but basically pip install django-cors-headers then add it to your INSTALLED_APPS and Middleware in settings.py
INSTALLED_APPS = [
...,
'corsheaders',
]
# make sure CorsMiddleware comes before CommonMiddleware
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]
Also use the #csrf_exempt decorator on the views you must be able to access (there are security implications to this but I am using JWT authentication anyways for this mobile app so this is okay in my case - I would do my due diligence here). There are a few ways to do this for function and class views:
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def my_view():
...
# or for class based views
class my_view(APIView):
#csrf_exempt
def dispatch(self, request, *args, **kwargs):
return super().dispatch(self, request, *args, **kwargs)
# or my preferred way for easy copy paste
#method_decorator(csrf_exempt, name='dispatch')
class my_view(APIView):
...
Expose the Django server over LAN, this step is necessary so that your device can interface with the Django server over the local network - may not be necessary if using an emulator (though I'm aware that the Android emulator for instance accesses localhost using a specially designated IP address so I would check for this).
# specify this IP to expose on LAN
python manage.py runserver 0.0.0.0:8000
Find your local machine's IP address - the blog above uses the mobile device's IP but that did not work for me, which makes sense given that both servers are running on my laptop. For windows I opened the command prompt and entered ipconfig then found the IPv4...... line which contains my address.
Use this IP to direct your API calls and add it to your CORS whitelist. For example if your localmachine IP is 10.0.0.1 and Expo is on port 19000 while Django is on port 8000 we add the following.
# Django settings.py
ALLOWED_HOSTS = ['10.0.0.1',]
CORS_ORIGIN_WHITELIST = [
'http://10.0.0.1:19000',
]
// Wherever you define the backend URL for the frontend, in my case config.js
export const BACKEND_URL = "http://10.0.0.1:8000"
// Then make requests
fetch(BACKEND_URL + "/api/endpoint/", ...

Nginx proxy pass is having 502/504 error and cannot reach the backend

I have a react project deployed inside an nginx docker container which is deployed on k8s which is on AWS.
I have a backend service which is deployed also on aws and I have a url to access it. It can be accessed directly on browser, on postman or my react app if I remove the proxy pass for this url, but I need the proxy pass to solve other issues.
It was working at the beginning and stopped working and I got 504, after trying every related solution from stack overflow, now I get 502 instead. And backend shows that nginx could never reach this backend.
There are many other backend Urls being used exactly the same way, and they are totally fine, it is just this one url which is not accessible.
I also have a local nginx running which never gives me issue on this backend Url, but even if I redeploy the react nginx server on aws and redeploy that backend service, it is still giving me 502. Any one have opinion on this weird issue?
Thank you for taking a look.
I tried every possible and related config for the locaton of this url in conf file. But none of them worked

My Django app passes authentication on localhost, but not on heroku

So I created a simple "social media website" where by using API I GET data from a database and I can also POST to create a social media post after I register and log in.
On my localhost it all works well. I can register, login, then write a social media post and it displays on the screen.
However, when I use Heroku, GET API works fine, but after I log in (and I am sure I am logged in as I can log in on admin), I cannot write anything on my website. In my IDE I get: Forbidden: /api/posts/action/
In the network page I can see this:
Request URL: http://localhost:8000/api/posts/action/
Request Method: POST
Status Code: 403 Forbidden
Remote Address: 127.0.0.1:8000
Referrer Policy: no-referrer-when-downgrade
Any idea where should I look for an error? If there is any code I should send, let me know. Thank you!
Your server's domain in Heroku shouldn't be localhost:8000
You need to use the correct domain/IP address, and remember to put the domain/IP address in ALLOWED_HOSTS in the setting file.
If you don't set up a custom domain, then the default domain should be like:
https://<dyno name>.herokuapps.com

Django and React app ERR_CONNECTION_REFUSED

I'm runing specfic configuration. I got django runing on apache on address localhost:8001 and react app running on nginx on localhost:8005. Also I use ngingx to route proxy. My biggest problem is when I try to call DRF API by React (calling http://127.0.0.1:8001/api-token-auth/ so I got / at the end of the endpoint for sure) I'm getting POST http://127.0.0.1:8001/api-token-auth/ net::ERR_CONNECTION_REFUSED
Backend is running fine, I can use endpoint from HTTPie or POSTMAN
When I call backend by external domain name in React like 'example.com/api-token-auth/' it's working just fine. How can I solve this?
Adding ALLOWED_HOSTS = ['127.0.0.1'] or ALLOWED_HOSTS = ['*'] may solve your issue of connection refused in your django settings.py file.
https://docs.djangoproject.com/en/3.0/ref/settings/#allowed-hosts
EDIT:
http://' + require("os").hostname() + ':8001/ or
http://' + require("ip").address() + ':8001/'
Try using this while building the localhost ip. People have fixed this issue with this change.

Django test client on an actual server

I'm testing deploying my first Django project using Apache.
I use Django's test client to perform an "internal" GET from my own server, which worked OK locally, but not runnning on the actual server.
The client ends up getting Django error messages, like
Page not found (404) Request Method: GET Request
URL: http://testserver/polls/forms/test1/
How can I get the client's GET to work on the actual server, having the it be performed on the actual http: //my_actual_server_name.something/polls/forms/test1 instead of "testserver" ?
I tried setting SERVER_NAME= ‘my_actual_server_name.something’ in the settings.py file but that's not it.
Django's test client doesn't actually make HTTP requests, it just makes a request object and passes it to your middleware/views.
If your goal is to make an http request to your own server, an easy way is to install requests and do something like
# Some server on the network
requests.get("http://myserver.com/polls/forms/test1/")
# or some server running on the same machine
requests.get("http://12.0.0.1:8000/polls/forms/test1/")
If you just want to use the functionality of some view, you should move that logic into a function and call that from both the view and your other code.
Very tangential side note:
If you're curious about how the test client doesn't make http requests, you can look at the test client's code in the django source (client.get() calls client.generic() which calls client.request() which instantiates WSGIRequest() and then passes that object to your app - which is the request that you receive in your views).