I'm sending a post request to my API made using django rest framework:
curl --header "X-MyHeader: 123" --data "test=test" http://127.0.0.1:8000/api/update_log/
In my rest framework view, I want to get my costum header, and if the custom header satisfies a condition, I will proceed to analyze my post data.
Ok, my view looks like:
class PostUpdateLogView(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (
parsers.FormParser,
parsers.MultiPartParser,
parsers.JSONParser,
)
renderer_classes = (renderers.JSONRenderer,)
def post(self, request):
print request.Meta
# Get custom header
# Validate custom header
# Proceed to analize post data
# Make response
content = {
'response': 'response',
}
return Response(content)
I'm trying to find my custom header on request.Meta element, but when I print request.Meta, I get a 500 error. If I print request.data, I get the expected response.
¿What is the way to get a custom header on my post request using django rest framework?
The name of the meta data attribute of request is in upper case:
print request.META
If your header is called "My-Header", your header will be available as:
request.META['HTTP_MY_HEADER']
Or:
request.META.get('HTTP_MY_HEADER') # return `None` if no such header
Quote from the documentation:
HTTP headers in the request are converted to META keys by converting all characters to uppercase, replacing any hyphens with underscores and adding an HTTP_ prefix to the name. So, for example, a header called X-Bender would be mapped to the META key HTTP_X_BENDER.
If you provide a valid header information and get that information from backend then follow those
client-name='ABCKD'
then you have get that client information in post or get function following this-
request.META['HTTP_CLIENT_NAME']
it will give you output 'ABCKD'.
remember that, whatever the valid variable name you provide in your header information in request, django convert it uppercase and prefix with 'HTTP_'
in here it will client-name converted to CLIENT_NAME and prefix with HTTP_.
so final output is HTTP_CLIENT_NAME
Seeing this is an old post, I thought I would share the newer and more flexible approach that is available since Django 2.2. You can use the request's headers object:
# curl --header "X-MyHeader: 123" --data "test=test" http://127.0.0.1:8000/api/update_log/
request.headers['X-MYHEADER'] # returns "123"
request.headers['x-myheader'] # case-insensitive, returns the same
request.headers.get('x-myheader') # returns None if header doesn't exist
# standard headers are also available here
request.headers.get('Content-Type') # returns "application/x-www-form-urlencoded"
The biggest differences with request.META are that request.headers doesn't prepend headers with HTTP_, it doesn't transform the header names to UPPER_SNAKE_CASE and that the headers can be accessed case-insensitively. It will only transform the header to Title-Casing (e.g. X-Myheader) when displayed.
Related
I'm trying to send an XML file as a payload over HTTP to a Django development server on my local machine. I have no issues sending HTTP POST requests with JSON payloads and I understand that XML functionality does not come with Django REST framework out of the box, so I installed djangorestframework-xml. I used a parser decorator in my api view function definition like so:
#api_view(['GET', 'POST'])
#parser_classes([XMLParser])
def my_view(request):
if request.method =='GET':
pass
if request.method == 'POST':
return Response({'received_data': request.data})
The issue is, the way the data is structured, the information I need to parse is stored as tag attributes, like so:
<CEF>
<Element>
<p x="1" y="2"/>
</Element>
</CEF>
And when it is received by Django, request.data does not include attribute tag data:
{
"received_data": {
"Element": {
"p": null
}
}
}
Is there a header I could add to the HTTP request to preserve the xml tag attribute data?
Any pointers are greatly appreciated!
I tried different configurations for the tag data, but ultimately, I have no control over the data source and the way the data is structured in the xml file.
I was able to find a solution for this, so a short description here in case someone ever needs it.
What worked for me was using the DRF built-in FileUploadParser rather than the XMLParser for my_view function. As per documentation, I included the appropriate header to the request: Content-Disposition: attachment; filename=upload.xml.
This allowed DRF to receive the entire XML file in the request, tags attributes included. The file can then be parsed via xml.etree.ElementTree, as was suggested by the first comment to the question.
I have the following function which makes a get request to a url.
def fetch_data(session = None):
s = session or requests.Session()
url = 'http://www.moldelectrica.md/utils/load4.php'
response = s.get(url)
print response.status_code
data = response.text
return data
I expect to get a string back in the form.
169,26,0,19,36,151,9,647,26,15,0,0,0,0,0,150,7,27,-11,-27,-101,-19,-32,-78,-58,0,962,866,96,0,50.02
But instead I get an empty unicode string. The status code returned is 200.
I've looked at the request headers but nothing in them suggests that any headers will require being set manually. Cookies are used but I think the session object should handle that.
Figured it out. As I said this url provides data for a display so it wouldn't normally be visited directly. Usually it would be requested by the display page and that page would provide a cookie.
So the solution is to make a request to the display url then reuse the session and make another request to the data url.
I have following code
class MyClass(restful.Resource):
def get(self):
headers = {'Content-Type': 'text/html'}
return make_response(render_template('myfile.html'),200,headers)
def post(self):
session['CONSUMER_KEY']=request.form.get('consumer_key')
session['CONSUMER_SECRET']=request.form.get('consumer_secret')
render_template('myfile.html')
api.add_resource(MyClass,"/mag/",endpoint="mag")
I have written following test:
def mytest(self):
content_type={"Content-Type": "application / x - www - form - urlencoded","Content-Disposition": "form-data"}
response = self.client.post(
api.url_for(MyClass), data = json.dumps({'consumer_key':'testconsumerkey',
'consumer_secret':'testconsumersecret'}),
headers=content_type
)
The issue is form data is blank and thats the values are not getting set in session. When i debug i see that request.data is populated but request.form is an empty dictionary. Can someone suggest how I can send form data in a post request from a test
EDIT: Environment details
Python 2.7, Flask web framework, self.client is . I am using flask.ext.testing
You seem to be confused as to what the expected format for the post body should be. Should it be JSON data (which is what you send in the test case), or should it be in the application/x-www-form-urlencoded format (which is what you claim to send in the test case, and what the endpoint will read)?
If you wish to receive JSON data, you'll need to change the endpoint to read the data from request.get_json(). You'll also need to use application/json as the Content-Type header in the test case.
If you wish to receive urlencoded post data, then just simplify the test case by removing the Content-Type header and the json.dumps. Just pass the data dict to the data argument.
I'm creating two POST calls. One using a django form and one using angular js via a resource xhr.
The angular setup looks like this:
myModule.factory('gridData', function($resource) {
//define resource class
var root = {{ root.pk }};
var csrf = '{{ csrf_token }}';
return $resource('{% url getJSON4SlickGrid root.pk %}:wpID/', {wpID:'#id'},{
get: {method:'GET', params:{}, isArray:true},
update:{method:'POST', headers: {'X-CSRFToken' : csrf }}
});
});
With creating an xhr post request as such:
item.$update();
This post request is send to the server as expected, but when I want to access the QueryDict I cannot access the data passed using:
name = request.POST.get('name', None)
name is always None like this.
The issue behind this is that the QueryDict object is getting parsed quite strange.
print request.POST
<QueryDict: {u'{"name":"name update","schedule":0"}':[u'']}>
Whereas I would have expected this result, which I got when I send the data via a "normal" Post request:
<QueryDict: {u'name': [u'name update'], u'schedule': [u'0']}>
So it seems to be that Django receives something in the POST request which instructs Django to parse the parameters into one string. Any idea how to circumvent this?
Update:
I found this discussion where they say that the issue is if you provide any content type other than MULTIPART_CONTENT the parameters will be parsed into one string. I checked the content-type send with the POST request and it is really set to 'CONTENT_TYPE': 'application/json;charset=UTF-8'. Thus this is likely the issue. Therefore my question is: How can I set the CONTENT_TYPE for a xhr post request created using angular.js resources to MULTIPART_CONTENT?
you could either:
fiddle with the client to send data instead of json
use json.loads(request.raw_post_data).get('name', None) (django < 1.4)
use json.loads(request.body).get('name', None) (django >= 1.4)
The Angular documentation talks about transforming requests and responses
To override these transformation locally, specify transform functions as transformRequest and/or transformResponse properties of the config object. To globally override the default transforms, override the $httpProvider.defaults.transformRequest and $httpProvider.defaults.transformResponse properties of the $httpProvider.
you can find an example here as was previously pointed at.
I'm working on a Django web application which (amongst other things) needs to handle transaction status info sent using a POST request.
In addition to the HTTP security supported by the payment gateway, my view checks request.META['HTTP_REFERER'] against an entry in settings.py to try to prevent funny business:
if request.META.get('HTTP_REFERER', '') != settings.PAYMENT_URL and not settings.DEBUG:
return HttpResponseForbidden('Incorrect source URL for updating payment status')
Now I'd like to work out how to test this behaviour.
I can generate a failure easily enough; HTTP_REFERER is (predictably) None with a normal page load:
def test_transaction_status_succeeds(self):
response = self.client.post(reverse('transaction_status'), { ... })
self.assertEqual(response.status_code, 403)
How, though, can I fake a successful submission? I've tried setting HTTP_REFERER in extra, e.g. self.client.post(..., extra={'HTTP_REFERER': 'http://foo/bar'}), but this isn't working; the view is apparently still seeing a blank header.
Does the test client even support custom headers? Is there a work-around if not? I'm using Django 1.1, and would prefer not to upgrade just yet if at all possible.
Almost right. It's actually:
def transaction_status_suceeds(self):
response = self.client.post(reverse('transaction_status'), {}, HTTP_REFERER='http://foo/bar')
I'd missed a ** (scatter operator / keyword argument unpacking operator / whatever) when reading the source of test/client.py; extra ends up being a dictionary of extra keyword arguments to the function itself.
You can pass HTTP headers to the constructor of Client:
from django.test import Client
from django.urls import reverse
client = Client(
HTTP_USER_AGENT='Mozilla/5.0',
HTTP_REFERER='http://www.google.com',
)
response1 = client.get(reverse('foo'))
response2 = client.get(reverse('bar'))
This way you don't need to pass headers every time you make a request.