Client might send multiple query params like:
# method = POST
http://api.com?x=foo&y=bar
I need to get all the query params and extract it as a string x=foo&y=bar from the POST request.
Is there a way to do this on fast api? I could not find it on their docs.
NOTE :
We are not sure of the names of the params before hand.
Depending how you want to use the data, you can either use request.query_params to retrieve a immutable dict with the parameters, or you can use request.url to get an object representing the actual URL. This object has a query property that you can use to get the raw string:
from fastapi import FastAPI, Request
import uvicorn
app = FastAPI()
#app.get('/')
def main(request: Request):
return request.url.query
uvicorn.run(app)
This returns the given query string /?foo=123 as foo=123 from the API. There is no difference between a POST or GET request for this functionality.
Related
I have the following python code in my Django views.py, the code takes in a JSON body and send the extracted DATA to another API endpoint, I have simplified the code here.
How do I enable csrf such that it will send the token back to the caller for this method? I am calling this from postman.
#csrf_protect
def validate_booking(request):
if request.method != "POST":
return HttpResponseServerError("Invalid HTTP method")
body = json.loads(request.body)
booking_details = body["booking_details"]
DATA = {
"name": booking_details["name"],
"nric": booking_details["nric"],
"booking_id": booking_details["booking_id"]
}
return HttpResponse(status="200")
This site directs to put this piece of code in my method. But what is "a_template.html"?
https://docs.djangoproject.com/en/4.1/ref/csrf/
#csrf_protect
def my_view(request):
c = {}
# ...
return render(request, "a_template.html", c)
This isn't an easy thing to do as CSRF is 2 steps thing
There is a value that is passed to the client and it is saved to the session on the server.
When a POST request is received, the client shall send this as csrfmiddlewaretoken in the body and the server will check the value against the stored one in the server's session.
So this isn't feasible to be done in APIs as you require session Management which is not of REST API implementations.
Thanks for your reply. I managed to find a solution by doing the following:
Create a new GET method that will generate the session CSRF token using python
Instead of using render which expects a HTML template file, I used JsonResponse(data) to return in JSON format directly
In my postman app which I am making the POST request with the X-CSRFToken in the header, I will first make a GET request to the new method I created in step 1 to retrieve the token and store it as an environment variable
The following is the GET method sample:
from django.http import JsonResponse
def get_csrf_token(request):
csrf_token = csrf(request)['csrf_token']
data = {'csrf_token': csrf_token}
return JsonResponse(data)
I'm trying to create a pydantic BaseModel that will be able to map some data from the request body and also from the request.state.
How can this be accomplished?
Request metadata shouldn't be part of your model - the model should only be concerned with the actual request (i.e. the data submitted by the user).
To access metadata about the request, add a Request object to your view definition:
def my_method(item: Item, request: Request):
This will magically give you all the metadata (including the .state entry) under the request variable in your view function.
Is there a method, or an attribute in request object that I can access to return me the URL exactly as the client requested? With the query params included?
I've checked request.build_absolute_uri after looking at this question but it just returns the URL without the query params.
I need the URL because my API response returns the URL for the "next page" of results. I could build it from the query_params attributes, but this view takes a lot of query params and some exclude others, so having access to the request url would save me a lot of pain.
To get full path, including query string, you want request.get_full_path()
I am building on Google App Engine + Python + webapp2. Part of building a modern web app requires a restful API. I know that I can do this with Flask, however I want to explore the possibility of building a REST API on webapp2.
On webapp2, requests are handled like this:
app = webapp2.WSGIApplication([
('/post/new', CreatePost),
('/post/([a-z0-9]+)', ViewPost),
('/post/([a-z0-9]+)/edit', EditPost),
('/post/([a-z0-9]+)/delete', DeletePost)
])
Note: ([a-z0-9]+) is a regex that represents the post_id
The above request handlers do not adhere to a RESTful pattern since the request methods are specified in the path (/delete, /edit, /new) rather than in the request headers.
Is the solution to create a single handler class that receives all request types? For example:
class PostHandler(webapp2.RequestHandler):
def get(self):
# handle GET requests
def post(self):
# handle POST requests
def put(self):
# handle PUT requests
def delete(self):
# handle DELETE requests
app = webapp2.WSGIApplication([
('/post/?', PostHandler)
])
In this case, all /post paths are handled by PostHandler. The post_id is no longer used in this pattern since it would be submitted in the request body instead.
Is this the correct approach to building a REST API with webapp2?
You are on the right path, but you should continue to handle post_id in the url and do it like this:
class PostHandler(webapp2.RequestHandler):
def get(self, post_id=None):
if post_id:
# handle Fetching a single post object
else:
# handle Queries
def post(self, post_id=None):
if post_id:
self.abort(405)
# handle creating a single post object
def put(self, post_id=None):
if post_id:
# handle updating a single post object
else:
self.abort(405)
def delete(self, post_id=None):
if post_id:
# handle deleting a single post object
else:
self.abort(405)
app = webapp2.WSGIApplication([
('/post/<post_id>/', PostHandler),
('/post/', PostHandler),
])
Furthermore putting the HTTP verb within the request payload like voscausa suggested is not inline with RESTful API design.
One should extend webapp2.RequestHandler and use the extension as the new base class for a RESTfulHandler and then maybe also for your specific purpose extend the RESTfulHandler to a RESTfulPostHandler. This way we could also deal with new ways or work such as jwt (JSON web token) in the header, authorization and other properties that you wish to handle in the header.
BTW, "post" might not be the best name for your item/object since it could easily get mixed up with the http verb "post". If I were you, I would rename it into "item" or similar, and use a RESTfulItemHandler which extends the webapp2 request handler. There are many things you would like to share between your handler and like that it is convenient to share the functionality in a base-class.
I have a Django application which must have the following behavior: if a request has no Content-Type header, it returns an error response.
In order to test this behavior, I need to make an HTTP request without a Content-Type header.
I am using the Client class in the django.test module. This has many methods, including this one:
post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra)
Makes a POST request on the provided path and returns a Response object, which is documented below.
[...]
If you provide content_type (e.g. text/xml for an XML payload), the contents of data will be sent as-is in the POST request, using content_type in the HTTP Content-Type header.
If you don’t provide a value for content_type, the values in data will be transmitted with a content type of multipart/form-data. In this case, the key-value pairs in data will be encoded as a multipart message and used to create the POST data payload.
The documentation says that a Content-Type header is always set on the request, irrespective of whether I pass a content_type argument.
So what other ways do I have to construct a request, such that it does not have a Content-Type header?
You can build a customised request instance through the class RequestFactory.
Once generated, you can modify the request instance before passing it to the view.
Using the example in the RequestFactory documentation page as a starting point, you can do:
from django.test import TestCase, RequestFactory
from .views import my_view
class SimpleTest(TestCase):
def setUp(self):
# Every test needs access to the request factory.
self.factory = RequestFactory()
def test_details(self):
# Create an instance of a POST request.
request = self.factory.post('/your/url', data={'your':'data'})
# NOW you can customise your request instance!
# (i.e. remove the Content-Type header)
request.META.pop('CONTENT_TYPE', None)
# Actually use the request to test my_view()
# as if it were deployed at /customer/details
response = my_view(request)
self.assertEqual(response.status_code, 400)
The request.META is just a standard Python dictionary (as explained here), so you can use
del request.META['CONTENT_TYPE']
instead of pop() to remove it, but only if you are deadly sure that the key will be in the dictionary.
I know this is several years old, but I had the same question and found the real answer, i.e. how to do this with the test client:
client.get(url, content_type=None)
At least on Django 2.0, that makes a request with no content type header.