I have a model that contains a FileField which may not be blank. When creating tests for this model, I've run into the problem that I get errors when testing with PUT, while the exact same thing works when doing a POST.
As views I'm simply using generics.ListCreateAPIView for the POST destination and generics.RetrieveUpdateDestroyAPIView for the PUT destination, both work normally when using the API in browser.
The payload for the POST and PUT is created as follows:
uploaded_file = SimpleUploadedFile('TestCode4.c', "Testcode", content_type='text/plain')
self.valid_payload = {
'name': 'TestValid',
'test_file': uploaded_file
}
Then the working POST test looks as follows:
client = Client()
response = client.post(
reverse('code-list'),
self.valid_payload,
format='json'
)
And the PUT:
client = Client()
response = client.put(
reverse('code-detail', kwargs={'pk': 1}),
self.valid_payload,
format='json'
)
The POST returns 204 and creates a new object, while the PUT returns 415 with the following error:
{u'detail': u'Unsupported media type "application/octet-stream" in request.'}
I am unsure what is going wrong here, it seems that both the post and put are passing the SimpleUploadedFile data in the same way, though with put it somehow becomes an octet stream.
I figured out the problem Django's django.test.Client class does not support the 'PUT' method. Instead the REST framework provides the class rest_framework.test.APIClient, which does support PUT (and PATCH, etc).
The client.put() function now needs to be filled in a little differently (I was unable to get it to work with SimpleUploadedFile) as explained here: https://fodra.github.io/2017/05/31/testing-django-rest-api-with-image-field.html
Related
The django rest framework forms related fields sometimes leak things I don't want. I want to write a test for it.
I tried this:
class CVRequestAPITests(APITestCase):
def test_disallow_read_access(self):
url = reverse('cvrequest-list')
response = self.client.get(url, headers={
'Accept': 'text/html,application/xhtml+xml'
})
But it still gives JSON.
I am trying to learn Angular2
and I am trying to create a simple blog with authentication.
this here is my add a new post method:
[Authorize]
// POST: api/Post
public PostModel Post([FromBody]PostViewModel model)
{
var post = new PostModel
{
Body = model.Body,
Title = model.Title,
AuthorId = IdentityExtensions.GetUserId(User.Identity),
};
var res = blogRepo.AddPost(post);
return res;
}
everything works fine, but IdentityExtension.GetUserId() do not return the most current logged in user but the first user since the app started.
basically I am looking for a way to make sure that the current user logs out on the server as well as on the client (the client side is just a simple removal of the localStorage.removeItem("jwt");)
also there is a good chance that what I am doing is totally wrong, but I can't access the ApplicationUserManager in this controller.
ok I have found the problem, although I haven't managed to solve it yet but I will update this when i do, and I am writing this as an answer since the problem is totally different from what I asked and thought to be.
the problem is related to sending the authentication hints as Thierry Templier suggested. I have a file that exports headers like this:
export const authHeaders = new Headers();
authHeaders.append('Accept', 'application/json');
authHeaders.append('Content-Type', 'application/json');
authHeaders.append('Authorization', 'Bearer ' + localStorage.getItem('jwt'));
And I Import this header where ever I need it. but I am not sure why it always sends a cached value (i.e the first value stored on the client and it has nothing to do with the server side as my question implies).
to solve this issue I just have to make sure the latest access-token stored on localstorage is sent to the server.
EDIT: for now I am constructing the headings in the constructor.
I'm working on an Ember.js app. I have an update function, part of an ObjectController.
The function should save my updated model, however when I call save(); it sends a POST request not a PUT request. (Tested in Chrome.)
Why would that happen? How can I make sure a PUT request is sent for updates?
Here is my code:
customer = this.get('model');
customer.set('name', 'New name');
customer.save();
For extra reference, when I log the "dirtyType" with console.log( customer.get('dirtyType') ); it says "updated".
Any help very much appreciated!
UPDATE
I've adjusted the sample code above to make it clearer, I am NOT creating a new model and wanting to use PUT. I have an existing model that I need to update.
I'm not sure if your workaround is correct in the land of PUT vs POST.
TL;DR PUT should define the resource (by Request-URI), but we don't do that during creation, so we shouldn't be using a POST. Override the create/save if you need this for your server, instead of hacking the isNew property, which may come back to bite you.
Put
9.6 PUT
The PUT method requests that the enclosed entity be stored under the
supplied Request-URI. If the Request-URI refers to an already
existing resource, the enclosed entity SHOULD be considered as a
modified version of the one residing on the origin server. If the
Request-URI does not point to an existing resource, and that URI is
capable of being defined as a new resource by the requesting user
agent, the origin server can create the resource with that URI. If a
new resource is created, the origin server MUST inform the user agent
via the 201 (Created) response. If an existing resource is modified,
either the 200 (OK) or 204 (No Content) response codes SHOULD be sent
to indicate successful completion of the request. If the resource
could not be created or modified with the Request-URI, an appropriate
error response SHOULD be given that reflects the nature of the
problem. The recipient of the entity MUST NOT ignore any Content-*
(e.g. Content-Range) headers that it does not understand or implement
and MUST return a 501 (Not Implemented) response in such cases.
If the request passes through a cache and the Request-URI identifies
one or more currently cached entities, those entries SHOULD be
treated as stale. Responses to this method are not cacheable.
The fundamental difference between the POST and PUT requests is
reflected in the different meaning of the Request-URI. The URI in a
POST request identifies the resource that will handle the enclosed
entity. That resource might be a data-accepting process, a gateway to
some other protocol, or a separate entity that accepts annotations.
In contrast, the URI in a PUT request identifies the entity enclosed
with the request -- the user agent knows what URI is intended and the
server MUST NOT attempt to apply the request to some other resource.
If the server desires that the request be applied to a different URI,
Custom Adapter
App.ApplicationAdapter = DS.RESTAdapter.extend({
createRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record, { includeId: true });
//return this.ajax(this.buildURL(type.typeKey), "POST", { data: data });
return this.ajax(this.buildURL(type.typeKey), "PUT", { data: data });
},
updateRecord: function(store, type, record) {
var data = {};
var serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record);
var id = get(record, 'id');
// you could do the same here, but it's even more incorrect
return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data });
},
});
http://www.ietf.org/rfc/rfc2616.txt
Thank you for all of your help guys, however I have found the issue and it is ridiculously silly.
The API I have been using had a new flag "is_new" and that had been added to the model and was overwriting the "isNew" property.
Causing Ember (and me) to get very confused.
I've tweaked the API and all is good in the world!
If the model was created with createRecord, and thus has isNew == true and you call save() the expected behavior is POST. Once the record has been persisted, and it is changed, and thus isDirty == true but isNew == false then the save() will be a PUT.
This is described in the Models Guide.
I have been trying to test my controllers as per this tutorial -> Zend Framework 2.1 Unit testing
I have tried every possible variation of the code to send POST or GET data along with the dispatch but the only thing I get back from running the test is "Undefined Index" when I try to access that data from the $_POST array in the controller.
I am using PHPUnit 3.7.17, Everything else works perfectly except for POST and GET data, I have tried the following code:
public function testIndexActionCanBeAccessed() {
$this->getRequest()
->setMethod("POST")
->setPost(new \Zend\Stdlib\Parameters(array('argument' => 'value')));
$response = $this->dispatch('/app/api/index');
$this->assertResponseStatusCode(200);
}
AND
public function testIndexActionCanBeAccessed() {
$post_data = array("argument" => "value");
$response = $this->dispatch("/app/api/index", "POST", $post_data);
$this->assertResponseStatusCode(200);
}
I can't find any help on the web how to fix this issue. Can anyone help out? Any ideas?
$p = new Parameters();
$p->set('username','foo');
$p->set('password','bar');
$this->getRequest()->setMethod('POST');
$this->getRequest()->setPost($p);
$this->dispatch('/login');
This works for me. The Parameters() constructor doesn't seem to be what you're expecting it to be.
The docs say it takes an array, but I could only get it to work this way. The Stdlib\Parameters() constructor doesn't seem to do anything with the array passed in.
Get/Post data not sending, this issue is related to server, can you check the function use for fetching data from server.
I'm trying to build a restful JSON api for my Symfony2 Application.
I'm using the http://jmsyst.com/libs/serializer JMS\Serializer Bundle to serialize my Entities to JSON.
I have this example Controller-Action:
public function getFarmerByNameAction(Request $request) {
$this->setLocale($request);
$name = $request->get("name");
$farmer = $this->getDoctrine()->getRepository("FarmerguideBackendBundle:Farmer")->findByName($name);
// Return json response
return new Response($this->jsonify($farmer));
}
Since I'm using this serializer very often (I know I should do something like a singleton or whatever, but currently I don't have the time for that, I was just playing with the framework) I've put the code inside a function which does the serializing.
private function jsonify($object) {
// Serialize to json
$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$json = $serializer->serialize($object, 'json');
return $json;
}
My problem is the following:
This code is inside a BackendController, which does NOT contain any gui-specific information. So just a RESTful API.
In another Controller, let's say WebappController I have the code to access these backendfunctions and do some stuff with twig-files and render()-methods.
I want to access all these information via mobile over ajax (therefore I need this json return value)
What's the best-practice here? Is it better to say: Well if it's a ajax-call (check with if($request->isXmlHttpRequest())) , do jsonify right before returning the repsonse and if it's not return the entities (I need entities for twig-templates..) Or is there another approach?
Or is it even better to work with $request->getFormatType() and making the ajax call with contentType="application/json; charset=utf-8"
Here is how KnpBundles handles it https://github.com/KnpLabs/KnpBundles/blob/master/src/Knp/Bundle/KnpBundlesBundle/Controller/DeveloperController.php#L35
I guess you need to clearify what your intentions are. Because right now it seems as if your WebappController would just be a client to your Backendcontroller. Something like:
$result = file_get_contents('/path/to/backend/method/1/3');
You then simply go ahead and decode the json.
That is some additional overhead of course. If you want to get entities, I would suggest to create a Service for all your backend methods and return the entities there. You then simply call those methods from your BackendController and your WebappController. You then would only jsonify the entities in your BackendController and render the appropriate templates in your WebappController.