Body of delete request is empty in my rest api endpoint - rest

I seem to get empty body content of a Go http.Request if the method is DELETE. But if I change the method to POST, then the body content gives me the content I expect.
The relevant code from my golang looks like this:
import(
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
func Delete(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
qs := r.Form
log.Println(qs)
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/profile", Delete).Methods("POST")
router.HandleFunc("/profile", Delete).Methods("DELETE")
}
Now when I run this JavaScript code form my browser:
fetch(sendurl,{
method:"POST",
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
},
body:"data="+project.encodeFormURIComponent(JSON.stringify({"ids":[1032,1033]}))
})
.then(response=>{
if(response.ok)
return response.json();
})
.then(result=>{
console.log(result);
})
I see a nice array of numbers in my qs[ids] in my Golang code. But if I change my method:"POST" to method:"DELETE" in the JavaScript, then qs is empty.
What am I doing wrong?
UPDATE
This JavaScript with the DELETE method can populate the Go qs variable the way one would normally expect:
fetch(sendurl+"?data="+project.encodeFormURIComponent(JSON.stringify({"ids":[1032,1033]})),{
method:"DELETE",
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(response=>{
if(response.ok)
return response.json();
})
.then(result=>{
console.log(result);
})
So it seems Go will ignore JavaScript body argument when DELETE method is used, but it will respect the query string content in the API endpoint url? Why is it like that?

https://www.rfc-editor.org/rfc/rfc7231#section-4.3.5
A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.
The query string is part of the target-uri of the request; in other words, the query string is part of the identifier, not an incidental modifier of it. But the message-body of the request is not part of the identifier.
So your local framework, or any of the other general purpose components forwarding your request, are not required to provide support for the message-body.
Think "undefined behavior" in C.

Related

Relay Modern BadRequestError: Missing multipart field ‘operations’

I am trying to upload file to my server. using Altair i can do it without any error but when i use Relay.js for uploading it server throws following error.
BadRequestError: Missing multipart field ‘operations’ (https://github.com/jaydenseric/graphql-multipart-request-spec).
at Busboy.<anonymous> (/home/dotsinspace/Documents/dev/truck.pe__server/node_modules/.pnpm/graphql-upload#9.0.0_graphql#15.3.0/node_modules/graphql-upload/processRequest.js:362:11)
at Object.onceWrapper (events.js:420:28)
at Busboy.emit (events.js:326:22)
at Busboy.EventEmitter.emit (domain.js:486:12)
at Busboy.emit (/home/dotsinspace/Documents/dev/truck.pe__server/node_modules/.pnpm/busboy#0.3.1/node_modules/busboy/lib/main.js:37:33)
at /home/dotsinspace/Documents/dev/truck.pe__server/node_modules/.pnpm/busboy#0.3.1/node_modules/busboy/lib/types/multipart.js:52:13
at processTicksAndRejections (internal/process/task_queues.js:75:11)
Following are my graphql code and mutation which i am trying to commit.
#Graphql
graphql`
mutation AccountUploadMutation($profilePicture: Image!) {
AccountUpload(profilePicture: $profilePicture) {
message,
status
}
}
`
#Mutation
commitMutation(Environment, {
'mutation': AccountUploadMutation,
'variables': { 'profilePicture': v },
'uploadables': { 'file': v },
'onCompleted': (response, error) => Response({}, { response, error })
})
and I am totally confused about uploading part to..in uploadables you have to provide file..but my server looks for variable with profilePicture as image how can i deal with it.
It looks like you have an issue the multipart parsing configuration in your backend.
My guess is that the Relay Network is sending your GraphQL query in the mutlipart field "operation", but your backend is looking for the field "operations" (plural). To fix the error, confirm that your Network is sending the query in the operations field, or change your backend to read whatever field it's actually being sent on.
Another possibility is you're not sending your query in the multipart format at all. If you followed the Network documentation's example for sending your request, then you are just sending a JSON object, not a multipart form:
// Example from Relay docs. Sends a JSON object, not a multipart
// form as expected by your backend
function fetchQuery(
operation,
variables,
cacheConfig,
uploadables,
) {
return fetch('/graphql', {
method: 'POST',
headers: {
// Add authentication and other headers here
'content-type': 'application/json'
},
body: JSON.stringify({
query: operation.text, // GraphQL text from input
variables,
}),
}).then(response => {
return response.json();
});
}
// Create a network layer from the fetch function
const network = Network.create(fetchQuery);
If this is the case, write your fetchQuery function to fetch data using a multipart form. See this example: fetch post with multipart form data

PUT Request not happening at all in Fantom

I am having some trouble with PUT requests to the google sheets api.
I have this code
spreadsheet_inputer := WebClient(`$google_sheet_URI_cells/R3C6?access_token=$accesstoken`)
xml_test := XDoc{
XElem("entry")
{
addAttr("xmlns","http://www.w3.org/2005/Atom")
addAttr("xmlns:gs","http://schemas.google.com/spreadsheets/2006")
XElem("id") { XText("https://spreadsheets.google.com/feeds/cells/$spreadsheet_id/1/private/full/R3C6?access_token=$accesstoken"), },
XElem("link") { addAttr("rel","edit");addAttr("type","application/atom+xml");addAttr("href","https://spreadsheets.google.com/feeds/cells/$spreadsheet_id/1/private/full/R3C6?access_token=$accesstoken"); },
XElem("gs:cell") { addAttr("row","3");addAttr("col","6");addAttr("inputValue","testing 123"); },
},
}
spreadsheet_inputer.reqHeaders["If-match"] = "*"
spreadsheet_inputer.reqHeaders["Content-Type"] = "application/atom+xml"
spreadsheet_inputer.reqMethod = "PUT"
spreadsheet_inputer.writeReq
spreadsheet_inputer.reqOut.writeXml(xml_test.writeToStr).close
echo(spreadsheet_inputer.resStr)
Right now it returns
sys::IOErr: No input stream for response 0
at the echo statement.
I have all the necessary data (at least i'm pretty sure) and it works here https://developers.google.com/oauthplayground/
Just to note, it does not accurately update the calendars.
EDIT: I had it return the response code and it was a 0, any pointers on what this means from the google sheets api? Or the fantom webclient?
WebClient.resCode is a non-nullable Int so it is 0 by default hence the problem would be either the request not being sent or the response not being read.
As you are obviously writing the request, the problem should the latter. Try calling WebClient.readRes() before resStr.
This readRes()
Read the response status line and response headers. This method may be called after the request has been written via writeReq and reqOut. Once this method completes the response status and headers are available. If there is a response body, it is available for reading via resIn. Throw IOErr if there is a network or protocol error. Return this.
Try this:
echo(spreadsheet_inputer.readRes.resStr)
I suspect the following line will also cause you problems:
spreadsheet_inputer.reqOut.writeXml(xml_test.writeToStr).close
becasue writeXml() escapes the string to be XML safe, whereas you'll want to just print the string. Try this:
spreadsheet_inputer.reqOut.writeChars(xml_test.writeToStr).close

PUT request returning a 400 Bad Request Error

I am doing a PUT request to RESTfull service which changes password of a user. For the time being I have just hardcoded values of new and old password in my AJAX test my service. However it is giving me a 400 error.
AJAX call
$.ajax({
type: "PUT",
url: "api/teachers/"+user,
data: {"old":"123","new":"qwe"},
contentType: "application/json",
success: function(data,status)
{
datax = data;
alert(data+status);
ko.applyBindings(new AddMarkSheetKo(data));
},
error: function(XMLHttpRequest, textStatus, errorThrown)
{
alert(XMLHttpRequest+textStatus+ errorThrown);
// error handler here
}
});
Restful function:
#PUT
#Path("/{name}")
#Consumes(MediaType.APPLICATION_JSON)
public Response changePwd(#PathParam ("name")String name,#QueryParam ("old") String old, #QueryParam("new") String nw){
System.out.println("entered function"+old+nw);
Teacher t = DataAccessUtil.getByName(Teacher.class, name);
if(t.getPassword().equals(old)){
t.setPassword(nw);
DataAccessUtil.update(t);
return Response.ok().build();
}
else{
return Response.status(Status.BAD_REQUEST).entity("Wrong password !!!").build();
}
//return reposnse;
}
This information might be useful that on the console it prints
entered functionnullnull
So it the restfull function is called however it is not receiving the query parameters.
Any help would be really appreciated!
First, you could replace the #QueryParam annotations with #FormParam ones to retrieve the 'new' and 'old' parameters of the PUT request. Then, you should remove the #Consumes("application/json") annotation and contentType:application/json from your server and browser side code, and finally replace the submitted data in JSON format into something like 'new=qwe&old=123'.
If you want to stay with a content in JSON format, you should probably map the incoming body with an entity (ie, a Java class annotated with JAXB annotations), so that the JAX-RS implementation you use could unmarshall the incoming JSON content into a Java object.
HTH.

How to get the REST response message in ExtJs 4?

I'm building upon RESTFul Store example of ExtJs 4. I'd like my script to display errors provided by the REST server, when either Add or Delete request fails. I've managed to obtain the success status of a request (see the code below), but how do I reach the message provided with the response?
Store:
var store = Ext.create('Ext.data.Store', {
model: 'Users',
autoLoad: true,
autoSync: true,
proxy: {
type: 'rest',
url: 'test.php',
reader: {
type: 'json',
root: 'data',
model: 'Users'
},
writer: {
type: 'json'
},
afterRequest: function(request, success) {
console.log(success); // either true or false
},
listeners: {
exception: function(proxy, response, options) {
// response contains responseText, which has the message
// but in unparsed Json (see below) - so I think
// there should be a better way to reach it than
// parse it myself
console.log(proxy, response, options);
}
}
}
});
Typical REST response:
"{"success":false,"data":"","message":"VERBOSE ERROR"}"
Perhaps I'm doing it all wrong, so any advice is appreciated.
I assume that your service follows the REST principle and uses HTTP status codes other than 2xx for unsuccessful operations.
However, Ext will not parse the response body for responses that do not return status OK 2xx.
What the exception/response object (that is passed to 'exception' event listeners) does provide in such cases is only the HTTP status message in response.statusText.
Therefore you will have to parse the responseText to JSON yourself. Which is not really a problem since it can be accomplished with a single line.
var data = Ext.decode(response.responseText);
Depending on your coding style you might also want to add some error handling and/or distinguish between 'expected' and 'unexpected' HTTP error status codes. (This is from Ext.data.reader.Json)
getResponseData: function(response) {
try {
var data = Ext.decode(response.responseText);
}
catch (ex) {
Ext.Error.raise({
response: response,
json: response.responseText,
parseError: ex,
msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
});
}
return data;
},
The reason for this behavior is probably because of the REST proxy class not being a first class member in the data package. It is derived from a common base class that also defines the behavior for the standard AJAX (or JsonP) proxy which use HTTP status codes only for communication channel errors. Hence they don't expect any parsable message from the server in such cases.
Server responses indicating application errors are instead expected to be returned with HTTP status OK, and a JSON response as posted in your question (with success:"false" and message:"[your error message]").
Interestingly, a REST server could return a response with a non-2xx status and a response body with a valid JSON response (in Ext terms) and the success property set to 'true'. The exception event would still be fired and the response body not parsed.
This setup doesn't make a lot of sense - I just want to point out the difference between 'success' in terms of HTTP status code compared to the success property in the body (with the first having precedence over the latter).
Update
For a more transparent solution you could extend (or override) Ext.data.proxy.Rest: this will change the success value from false to true and then call the standard processResponse implementation. This will emulate 'standard' Ext behavior and parse the responseText. Of course this will expect a standard JSON response as outlined in your original post with success:"false" (or otherwise fail).
This is untested though, and the if expression should probably be smarter.
Ext.define('Ext.ux.data.proxy.Rest', {
extend: 'Ext.data.proxy.Rest',
processResponse: function(success, operation, request, response, callback, scope){
if(!success && typeof response.responseText === 'string') { // we could do a regex match here
var args = Array.prototype.slice.call(arguments);
args[0] = true;
this.callParent(args);
} else {
this.callParent(arguments);
}
}
})

What's the best way to handle a REST API's 'create' response in Backbone.js

I'm using backbone.js to interact with a REST API that, when posting to it to create a new resource, responds with a status of 201, a 'Location' header pointing to the resource's URI, but an empty body.
When I create a new model at the moment, its successful, but the local representation of the model only contains the properties I explicitly set, not any of the properties that would be set on the server (created_date, etc.)
From what I understand, Backbone would update its representation of the model with data in the body, if there were any. But, since there isn't, it doesn't.
So, clearly, I need to use the location in the Location header to update the model, but what's the best way to do this.
My current mindset is that I would have to parse the url from the header, split out the id, set the id for the model, then tell the model to fetch().
This seems really messy. Is there a cleaner way to do it?
I have some influence over the API. Is the best solution to try to get the API author to return the new model as the body of the response (keeping the 201 and the location header as well)?
Thanks!
Sounds like you will have to do a little customization.
Perhaps override the parse method and url method of your model class inherited from
Backbone.Model.
The inherited functions are:
url : function() {
var base = getUrl(this.collection);
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
parse : function(resp) {
return resp;
},
and you could try something like:
parse: function(resp, xhr) {
this._url = xhr.getResponseHeader('location')
return resp
}
url: function() {
return this._url
}
Yes, backbone.js really wants the result of a save (be it PUT or POST) to be a parseable body which can be used to update the model. If, as you say, you have influence over the API, you should see if you can arrange for the content body to contain the resource attributes.
As you point out, its makes little sense to make a second over-the-wire call to fully materialize the model.
It may be that a status code of 200 is more appropriate. Purists may believe that a 201 status code implies only a location is returned and not the entity. Clearly, that doesn't make sense in this case.
With Backbone 0.9.9, I couldn't get the accepted answer to work. The signature of the parse function seems to have changed in an older version, and the xhr object is no longer available in the function signature.
This is an example of what I did, to make it work with Backbone v0.9.9 and jQuery 1.8.3 (using a Deferred Object/Promise), relying on the jqXHR object returned by Backbone.Model.save() :
window.CompanyView = Backbone.View.extend({
// ... omitted other functions...
// Invoked on a form submit
createCompany: function(event) {
event.preventDefault();
// Store a reference to the model for use in the promise
var model = this.model;
// Backbone.Model.save returns a jqXHR object
var xhr = model.save();
xhr.done(function(resp, status, xhr) {
if (!model.get("id") && status == "success" && xhr.status == 201) {
var location = xhr.getResponseHeader("location");
if (location) {
// The REST API sends back a Location header of format http://foo/rest/companys/id
// Split and obtain the last fragment
var fragments = location.split("/");
var id = fragments[fragments.length - 1];
// Set the id attribute of the Backbone model. This also updates the id property
model.set("id", id);
app.navigate('companys/' + model.id, {trigger: true});
}
}
});
}
});
I did not use the success callback that could be specified in the options hash provided to the Backbone.Model.save function, since that callback is invoked before the XHR response is received. That is, it is pointless to store a reference to the jqXHR object and use it in the success callback, since the jqXHR would not contain any response headers (yet) when the callback is invoked.
Another other to solve this would be to write a custom Backbone.sync implementation, but I didn't prefer this approach.