django block or redirect all requests - django

I would like to block or redirect all requests to another service temporarily in django request/response module. However, I do want to do this put a control mechanism at the begining of all service functions. For example there is a sginal request_start which is sent when a request incomes to any restful API. In handler, is it possible to also block these requests or stop django temporarily?

If you want to simply filter requests by context (for example by the URL) or reject all of them, then you can write your own middleware with a process_request method, where you can check for condition(s) and return either None (to continue processing) or HttpResponse with redirect/404/403 (to block processing).
Now, if you want to send signals or perform any other processing, you can do it in another middleware and simply set a proper order in MIDDLEWARE_CLASSES (the "blocking" middleware should be the last).

Related

how to send "messagebody" directly in api call to SQS queue via url without using postman?

i have api gateway with 3 simple backends:
2 basic api routes (/plus and /minus) backed by lambda functions
1 direct sqs queue (/sqs_send)
It means i can send via api call directly to my sqs queue.
2 lambda backend functions take 2 params 'a,b' from api call and add,subtract and show output.
https://86bwtlv5ya.execute-api.us-east-1.amazonaws.com/minus?a=10&b=20 # prints -10 in browser
https://86bwtlv5ya.execute-api.us-east-1.amazonaws.com/plus?a=10&b=20 # prints 30 in browser
The 3rd function is tricky for me. Via postman i managed to send directly to sqs like this via put request. Notice how i have to select "body" "raw" then input message. I did check the sqs queue - the msg from postman is there.
My question - what to type into my api gateway endpoint to send msg directly to sqs? Without using postman?
https://86bwtlv5ya.execute-api.us-east-1.amazonaws.com/sqs_send?mesagebody # what to type after sqs_send?
This did not work - returns {"message":"Not Found"}
https://86bwtlv5ya.execute-api.us-east-1.amazonaws.com/sqs_send?
Action=SendMessage&
MessageBody=This+is+a+test+message
Is it possible, my sqs_send api route does not work with parameters, because it is designed to only work with "messagebody" as per my settings? "Message attributes" is empty?
If I understood correctly, you want to call your API gateway endpoints by directly entering the URL into your browser's address bar.
Short answer:
Unfortunately you can't do this with your 3rd endpoint /sqs_send, because it is a PUT endpoint which the browser cannot call directly through the address bar.
Details:
Browsers usually support only HTTP GET and POST methods directly, through form submissions (which in turn is an HTML limitation, where form submissions only support these two methods). In GET method, parameters are appended to the end of the URL in the pattern example.com/?name1=value1&name2=value2. In POST method, parameters are included in the body of the request, so they're not visible in the URL itself. This means that you can only call GET endpoints by directly typing into the address bar of your browser. Your /plus and /minus are likely GET endpoints. POST endpoints must be called via HTML form submissions to include your parameters.
For calling other methods like PUT and DELETE (in addition to GET and POST), you will have to use XMLHttpRequest or the Fetch API, or some frontend framework method built around them. As your Postman screenshot shows, your third endpoint /sqs_send is a PUT, so you can't call it directly by entering the URL into the browser's address bar. If you must call it this way, you will have to convert your endpoint to a GET so that you can send your parameters via URL parameters.

Django UpdateCacheMiddleware at the beginning and FetchFromCacheMiddleware at the end

According to Django recommendation UpdateCacheMiddleware should be at the beginning of the middlewares list while FetchFromCacheMiddleware should be at the end.
I was wondering, doesn't that mean that when I save a response to the cache, it will be AFTER is passed through all of the middlewares (in the request phase and then again in the response phase),
but when I fetch a response from the cache, it will be already AFTER it passed through all of the middlewares, and then it will go back AGAIN through all of the middlewares in the response phase?
Does it mean that all middlewares should be able to receive a response that they already processed?
So there's two methods which apply to what you're talking about in middleware.
You have process_request and then process_response.
As you make a request, django goes through the middleware from the top, to the bottom calling process_request. Then it hits the view, and on the way back with the response the middleware is processed from the bottom to the top, calling process_response.
Specific to your examples with cache, FetchFromCacheMiddleware contains process_request and UpdateCacheMiddleware contains process_response.
To quote the docs...
Middleware order and layering
During the request phase, before calling the view, Django applies middleware in the order it’s defined in MIDDLEWARE, top-down.
You can think of it like an onion: each middleware class is a "layer" that wraps the view, which is in the core of the onion. If the request passes through all the layers of the onion (each one calls get_response to pass the request in to the next layer), all the way to the view at the core, the response will then pass through every layer (in reverse order) on the way back out.
If one of the layers decides to short-circuit and return a response without ever calling its get_response, none of the layers of the onion inside that layer (including the view) will see the request or the response. The response will only return through the same layers that the request passed in through.
The docs are here; https://docs.djangoproject.com/en/3.1/topics/http/middleware/#middleware-order-and-layering
Django middle ware works like a sandwich:
We have two main parts to run for each middleware
Request from user
UpdateCache.process_request (no much action here)
FetchCache.process_request (all action is here) ,The Cache can return a response here
actual Views
FetchCache.process_request (no much action here)
UpdateCache.process_request (the cache is updated with the fresh response here)
response to user
So UpdateCache is put at the first, to be run at the end of the cycle.

Redirecting API requests in Django Rest Framework

I have a two-layer backend architecture:
a "front" server, which serves web clients. This server's codebase is shared with a 3rd party developer
a "back" server, which holds top-secret-proprietary-kick-ass-algorithms, and has a single endpoint to do its calculation
When a client sends a request to a specific endpoint in the "front" server, the server should pass the request to the "back" server. The back server then crunches some numbers, and returns the result.
One way of achieving it is to use the requests library. A simpler way would be to have the "front" server simply redirect the request to the "back" server. I'm using DRF throughout both servers.
Is redirecting an ajax request possible using DRF?
You don't even need the DRF to add a redirection to urlconf. All you need to redirect is a simple rule:
urlconf = [
url("^secret-computation/$",
RedirectView.as_view(url=settings.BACKEND_SECRET_COMPUTATION_URL))),
url("^", include(your_drf_router.urls)),
]
Of course, you may extend this to a proper DRF view, register it with the DRF's router (instead of directly adding url to urlconf), etc etc - but there isn't much sense in doing so to just return a redirect response.
However, the code above would only work for GET requests. You may subclass HttpResponseRedirect to return HTTP 307 (replacing RedirectView with your own simple view class or function), and depending on your clients, things may or may not work. If your clients are web browsers and those may include IE9 (or worse) then 307 won't help.
So, unless your clients are known to be all well-behaving (and on non-hostile networks without any weird way-too-smart proxies - you'll never believe what kinds of insanity those may do to HTTP requests), I'd suggest to actually proxy the request.
Proxying can be done either in Django - write a GenericViewSet subclass that uses requests library - or by using something in front of it, e.g. nginx or Caddy (or any other HTTP server/load balancer that you know best).
For production purposes, as you probably have a fronting webserver, I suggest to use that. This would save implementation time and also a little bit of server resources, as your "front" Django project won't even have to handle the request and keep the worker busy as it waits for the response.
For development purposes, your options may vary. If you use bare runserver then a proxy view may be your best option. If you use e.g. Docker, you may just throw in an HTTP server container in front of your Django container.
For example, I currently have a two-project setup (legacy Django 1.6 project and newer Django 1.11 project, sharing the same database) and a Caddy server in front of those, routing on per-URL basis. With a simple 9-line Caddyfile things just work:
:80
tls off
log / stdout "{common}"
proxy /foo project1:8000 {
transparent
}
proxy / project2:8000 {
transparent
}
(This is a development-mode config.) If you can have something similar, then, I guess, that would be the simplest option.

Best way to cache statistics that don't change often

I am writing a django app and as part of the job I need to crunch some data and generate statistics and serve them on a RESTful API. The statistics don't change that often, however when the statistics do change, the next request needs to serve the most up to date request. What I am currently doing is using a caching mechanism like django-redis to store the statistics and when a request is made, the view calls the cache client and serve its content. What I would prefer is a caching mechanism that prevents my view from ever being called and also provides up to date content. Is there such away (django plugin) that would allow me to do this?
One way to accomplish this would be to use a 'reverse proxy cache' such as Nginx or Varnish.
Basically when a request would be made to your Django application, it would first pass through your proxy cache. The proxy cache would check to see if the request is available in the cache, and if so, it would serve the response from cache. If the request isn't in the cache, it would hand the request off to django to process te request and issue a response. The response would then pass back through the proxy cache and set the contents of the response into cache so that subsequent requests use the response from the cache.
Invalidating items in the cache per a write through policy as updating an item in the database could accomplished by issuing a cache purge command that is specific to the reverse proxy cache server that you have installed.

Django redirect to site root with variable....?

I'm trying to redirect the browser back to the site root and also pass a variable in order to trigger a JS notification function... This is all with Django.
What I have now is this:
urls.py:
url(r'^accounts/password/reset/complete/$', views.passwordResetComplete,
name='password_reset_complete'),
views.py:
def passwordResetComplete(theRequest):
return redirect(home(theRequest, 'Password reset successful'))
def home(theRequest, myMessage=None):
.........
return render_to_response('new/index.html',
{
"myTopbar": myTopbar,
"isLoggedIn": isLoggedIn,
"myMessage": myMessage
},
context_instance=RequestContext(theRequest)
)
I get this error:
NoReverseMatch: Error importing 'Content-Type: text/html; charset=utf-8.......(gives full HTML of page)
I've been working around a few different solutions and nothing seems to work in the way that I need. The closest I've got is to redirect to '/?query-string' with a JS function in root to check for that query-string and run the function if it's present. However, that leaves the query-string in the URL for the duration of the user's navigation of the site (which is 100% AJAX). I want to avoid having any strings/long hrefs in the URL.
Would be really grateful if anyone can tell me how to solve this problem.
HTTP is a stateless protocol, which means that each and every request is entirely unique and separated from anything and everything that has ever been done before. Put more simply, the only way (in HTTP) to "pass a variable" with a URL is to add it to the URL itself (/someobject/1/, for example, where 1 is an object id) or in the querystring (?someobject=1). Either way, the information is embedded in the URL and it's up to your application to decipher that information out of the URL and do something with it.
The concept of a "session" was introduced as a way to provide state to the stateless protocol that is HTTP. The way it works is that the server sends the client a cookie containing some identifiable information (usually, just a session id). Then, the client sends the cookie back to the server in the request headers with every request. The server sees the cookie, looks up the session and continues on seamlessly with whatever is in progress. This is not true state, but it does provide the ability to essentially mimic state, and it's the only way to pass data between requests without actually embedding the data in the URL.
If all you need to return back is a message to the user such as "Password reset successful", you can and should simply use Django's messages framework, which itself uses the session pass the message. It sets a cookie for the client, so that you can redirect to any URL. The cookie will be passed back with the request for that new URL, and Django will add the message from the session into the appropriate place in your template for that URL.
If you need to actually invoke a bit of JavaScript, then you should make the request via AJAX. In the response, you can return any data you want in via JSON (and act on that data however you like) or even return Javascript to be run.
Following the redirect docs, you cannot simply redirect to a view, but only to a url or an object/view that is a assigned to a url already. Thus, you have 2 options:
a) Call the view directly like that:
return home(theRequest, 'Password reset successful')
b) Add a Url patterns like that:
url(r'^your_patterns/$', views.home, msg='',name='home'),
Then you will be able to do what you initally did:
return redirect(views.home,('Password reset successful',))
or from my point of view, even tidier:
return redirect('home',('Password reset successful',))