What request to use to update resource without sending data - http

every now and then I come to this question, google around for some time without getting a definitive answer and let it be.
Problem:
I want to update an existing resource, but the logic how this update works is on the backend side.
Let's assume a product should only be visible for a set time. Keeping it simple. The time "from" is stored in the database along the product data: product.visibleFromDate and the duration (eg. 30 days) is just a variable or configured somewhere (not in DB).
Now I want to call the backend telling it to update the visibleFromDate to "now": /api/product/:id/updatevisibility
In this case I don't want to send a body because the server should determine what value "now" really is.
I actually don't really care if the server answers with the updated resource or no content.
HTTP Request
GET
Should not be used because it should be idempotent, which it wouldn't be because the visibleFromDate would be updated
POST
Don't want to send the date from frontend
PUT
Don't want to send the date from frontend + should be idempotent
PATCH
Merge Patch: Don't want to send the date from frontend
JSON Patch: Again, I don't want to send a value, I want the backend to determine it.
Of course I could just send an empty object, the whole resource or some nonsense and ignore it on the backend side, but still I feel like I am missing this "trigger" type of requests which only need the type of resource, id and action.

Depending on the mutability of the data it should either be POST(immutable) or PATCH(mutable). It doesn't depend on what You're sending or not sending.
If You really want to do it by the book then you should send a '{}' when there are no fields to send. If any is added later you will just add it like '{"duration":"30"}'

You would definitely want POST over GET for authorization/authentication purposes.
You don't need to have a body for a POST request. Your endpoint will just listen for a request, it will extract :id, and then run your time update function.
PUT and PATCH are semantic equivalents to POST. Unless you programmed the backend to differentiate them, there won't be any difference. You don't even need them at all.
A single empty-body POST will be enough for your endpoint. But you may consider using empty-body PATCH just as a meaningful endpoint. In both cases, your backend will extract the :id and then just run the same function (after auth* if any)
EDIT: Seems not all people can grasp what I have, so here are a working nodejs/express backend and curl requests to use. you don't need a request body, and all behave the same, except semantics and security over GET.
this is the server code:
const express = require('express');
const app = express();
const port = 3000
const updatetime = async (id)=>{
let newdate=Date.now();
console.log(`updating resource ${id} to have a new date ${newdate}`);
return Promise.resolve("done")
}
const processrequest=async (req, res) => {
console.log(req.method);
console.log(req.headers);
console.log(req.headers.authorization);
console.log(req.body)
console.log(req.params)
try{
let result=await updatetime(req.params.id);
console.log(result);
res.status(200).send("time updated")
}catch{
res.status(500).send("something went wrong")
}
}
app.get( '/:id/updatetime', (req, res) => processrequest(req,res))
app.post( '/:id/updatetime', (req, res) => processrequest(req,res))
app.put( '/:id/updatetime', (req, res) => processrequest(req,res))
app.patch('/:id/updatetime', (req, res) => processrequest(req,res))
app.listen(port, () => {
console.log(`listening on port ${port}`)
})
test all endpoint without a body, all works because magic:
curl -X GET -H "Authorization: Basic" localhost:3000/123qwe/updatetime
time updated
curl -X POST -H "Authorization: Basic" localhost:3000/123qwe/updatetime
time updated
curl -X PUT -H "Authorization: Basic" localhost:3000/123qwe/updatetime
time updated
curl -X PATCH -H "Authorization: Basic" localhost:3000/123qwe/updatetime
time updated
server output for all request types are the same because it is just a magic(!) that is developers' choice of implementation:
listening on port 3000
GET/POST/PUt/PATCH
{
host: 'localhost:3000',
'user-agent': 'curl/7.78.0',
accept: '*/*',
authorization: 'Basic'
}
Basic
undefined
{ id: '123qwe' }
updating resource 123qwe to have a new date 1656495470330
done

You are not required to pass a body in a request (Although the specification says so), PATCH will be your best option here since semantically it is updating an “part of existing resource” while PUT is meant for "updating the entire resource".

my other answer did not satisfy you, so i will try the same but from a different viewpoint.
you are data engineer:
you don't retrieve any data with this operation, so no GET.
you operate on already-existing data, so no POST.
you don't change the whole data, so no PUT.
you are partially changing the data, see, you just use PATCH for the operation.
you will send an empty body {} to be consistent
you are an operational backend engineer.
GET is still considered to be used to retrieve a resource.
you want to run a function on the server which will partially modify the resource having the id given as the parameter in the url, so just use a POST request. it is like calling a function in your programming language.
or try PATCH to get the sympathy of your users.
or use PUT to get their anger when they realized what happens.
it is already getting the id from the URL, so you don't have to send any body unless your frontend API forces. curl won't send a body if you don't give one, for example.
semantics are not hard-coded rules. they are there to guide you and keep you in the line as much as possible. the problem is that they don't have solid implementation details. they are also "opinions" of a group of people which has pretty fine shape anyone can accept.
PS: It is said, "POST can be used on an already-existing data to append". sure it is, but the question in scope asks about changing a field, not creating a new one.

Related

Is GitHub CLI "--jq" argument (to select values from the response) processed on the server or on the client?

according to examples from https://cli.github.com/manual/gh_api:
# print only specific fields from the response
$ gh api repos/{owner}/{repo}/issues --jq '.[].title'
GitHUb CLI (gh) can use the '-q' or '--jq' argument followed by a query string to select values from the response and e.g. display only certain fields.
I'd like to do a similar thing using either CURL, postman or JavaScript fetch().
So my question is: is the --jq query string sent (somehow) by gh CLI as part of an http request and processed on the server (to reduce the amount of data in the response), or is it only applied on the client side as post-processing of the data received ?
And if that query string can be passed to and processed on the server, how should it be specified if I need to make the request not with gh but with curl, postman or javascript fetch() method?
I fear the processing is actually done on the client, meaning the http response will always provide the full data...
Can someone confirm this?
Thanks!
The response is filtered on the client. You can verify yourself by taking a look at the source code:
if opts.FilterOutput != "" && serverError == "" {
// TODO: reuse parsed query across pagination invocations
err = jq.Evaluate(responseBody, bodyWriter, opts.FilterOutput)
if err != nil {
return
}
}
curl, postman, fetch
Your goal is to make an HTTP Request, not use the gh CLI.
GitHub's GraphQL API is what you seek:
To create integrations, retrieve data, and automate your workflows, use the GitHub GraphQL API. The GitHub GraphQL API offers more precise and flexible queries than the GitHub REST API.
GraphQL only returns the data requested. The data is "processed" on the API server and then sent to the client.

What is the need of HTTP methods like get,post in HTTP protocol?

can we not implement HTTP protocol using just request body and response body? For example, the URL will contain request, which will be sent to a server and will be mapped to some function on server and in response html/java script response will be sent across What is really the need of HTTP methods?
PS: Please read my comment on the first answer. That will clarify my question more.
Basically, these are the terminologies which describe the Attribute of HTML Request. It means that the request will tell you about the particular function that you perform in a certain task.
There are four basic functions which we also called CRUD operations.
Create => POST
Read => GET
Update => PUT
Delete => DELETE
As the above description shows that each an every Attribute have certain functionality.

Can't send POST request to Google Apps Script

I'm trying to deploy a Google Apps Script as a web app, but while I have no problem doing GET requests, I'm having trouble with POST requests.
My code is very simple:
function doGet(request) {
var result = JSON.stringify({ data: 'Thanks, I received the GET request' });
return ContentService.createTextOutput(result).setMimeType(ContentService.MimeType.JSON);
}
function doPost(request) {
var result = JSON.stringify({ data: 'Thanks, I received the POST request' });
return ContentService.createTextOutput(result).setMimeType(ContentService.MimeType.JSON);
}
I deployed the web app with "Execute the app as: Me" and "Who has access to the app: Anyone, even anonymous". Every time I do some change I re-deploy it with a new version ("Project version: New").
After publishing, my curl GET request works perfectly:
> curl -L https://script.google.com/macros/s/$SCRIPT_ID/exec
{"data":"Thanks, I received the GET request"}
However, my POST request (curl -L -XPOST https://script.google.com/macros/s/$SCRIPT_ID/exec) just shows me a generic Google HTML page saying "Sorry, unable to open the file at this time. Please check the address and try again".
I tried sending some data and providing a content type, but nothing changed. I also tried changing the output type to just ContentService.createTextOutput("OK"), but it didn't work either. Curiously, deleting doPost changes the error message to "Script function not found: doPost" as expected. If it makes any difference, this script is attached to a Google spreadsheet.
Are there any special permissions I need to give to the script for POST requests specifically?
It seems that the problem was with my usage of curl, on subtle differences between using -XPOST and not using it. As suggested by Tanaike, changing from:
curl -L -XPOST https://script.google.com/macros/s/$SCRIPT_ID/exec
to
curl -L -d '' https://script.google.com/macros/s/$SCRIPT_ID/exec
Solved the issue. Even though curl helpfully says "Unnecessary use of -X or --request, POST is already inferred" when I do a request with -XPOST and a payload, its behavior is different in the presence of redirects. -XPOST forces all subsequent requests after a redirect to be made using POST as a method. On the other hand, if I don't specify -XPOST, the requests after the first POST are made as GET requests. I don't know if this is curl's intended behavior, but it's certainly unintuitive.

Nginx Lua - rewrite url, forward original POST data/multipart form data

Nginx with its Lua extension makes it very easy to examine and manipulate a request prior to having it dispatched as appropriate. In my current project I am trapping all requests that arrive at a specified folder location, examining the request and then having it executed by an entirely different script. An outline of what I do:
/etc/nginx/sites-available/default Configuration
location /myfolder{
rewrite_by_lua_file "/path/to/rewrite.lua";
lua_need_request_body "on";
}
In rewrite.lua
ngx.req.set_header('special','my_special-header');
local data = ngx.req.get_body_data();
ngx.req.set_body_data(data);
and finally redirecting to another location:
ngx.req.set_uri("/myother/index.php",true);
With simple GET requests or POST requests with one or two items of attached POST data this works well. The issue I have been unable to resolve is this. Say for instance I am sending out multipart from data in my original request.
ngx.req.get_body_data()
actually gets the raw request body. If I forward this to /myother/index.php I can retrieve this as file_get_contents('php://input'). This is OK but not good enough. I don't want to have to deal with the raw input here. I would rather be able to work with standard PHP $_POST and $_FILES variables. However, those are empty and the their contents are present in the body as a text string.
Is there a way to tell Nginx that when I subject a user request to some treatment by Lua prior to forwarding it to another URL, it should just pass on the POST/PUT request fields as well as the whole of the original $_FILES array?

I keep receiving "invalid-site-private-key" on my reCAPTCHA validation request

maybe you guys can help me with this. I am trying to implement
reCAPTCHA in my node.js application and no matter what I do, I keep
getting "invalid-site-private-key" as a response.
Here are the things I double and double checked and tried:
Correct Keys
Keys are not swapped
Keys are "global keys" as I am testing on localhost and thought it might be an issue with that
Tested in production environment on the server - same problem
The last thing I can think of is that my POST request to the reCAPTCHA
API itself is incorrect as the concrete format of the body is not
explicitly documented (the parameters are documented, I know). So this
is the request body I am currently sending (the key and IP is changed
but I checked them on my side):
privatekey=6LcHN8gSAABAAEt_gKsSwfuSfsam9ebhPJa8w_EV&remoteip=10.92.165.132& challenge=03AHJ_Vuu85MroKzagMlXq_trMemw4hKSP648MOf1JCua9W-5R968i2pPjE0jjDGX TYmWNjaqUXTGJOyMO3IKKOGtkeg_Xnn2UVAfoXHVQ-0VCHYPNwrj3PQgGj22EFv7RGSsuNfJCyn mwTO8TnwZZMRjHFrsglar2zQ&response=Coleshill areacce
Is there something wrong with this format? Do I have to send special
headers? Am I completely wrong? (I am working for 16 hours straight
now so this might be ..)
Thank you for your help!
As stated in the comments above, I was able to solve the problem myself with the help of broofa and the node-recaptcha module available at https://github.com/mirhampt/node-recaptcha.
But first, to complete the missing details from above:
I didn't use any module, my solution is completely self-written based on the documentation available at the reCAPTCHA website.
I didn't send any request headers as there was nothing stated in the documentation. Everything that is said concerning the request before they explain the necessary parameters is the following:
"After your page is successfully displaying reCAPTCHA, you need to configure your form to check whether the answers entered by the users are correct. This is achieved by doing a POST request to http://www.google.com/recaptcha/api/verify. Below are the relevant parameters."
-- "How to Check the User's Answer" at http://code.google.com/apis/recaptcha/docs/verify.html
So I built a querystring myself (which is a one-liner but there is a module for that as well as I learned now) containing all parameters and sent it to the reCAPTCHA API endpoint. All I received was the error code invalid-site-private-key, which actually (as we know by now) is a wrong way of really sending a 400 Bad Request. Maybe they should think about implementing this then people would not wonder what's wrong with their keys.
These are the header parameters which are obviously necessary (they imply you're sending a form):
Content-Length which has to be the length of the query string
Content-Type which has to be application/x-www-form-urlencoded
Another thing I learned from the node-recaptcha module is, that one should send the querystring utf8 encoded.
My solution now looks like this, you may use it or built up on it but error handling is not implemented yet. And it's written in CoffeeScript.
http = require 'http'
module.exports.check = (remoteip, challenge, response, callback) ->
privatekey = 'placeyourprivatekeyhere'
request_body = "privatekey=#{privatekey}&remoteip=#{remoteip}&challenge=#{challenge}&response=#{response}"
response_body = ''
options =
host: 'www.google.com'
port: 80
method: 'POST'
path: '/recaptcha/api/verify'
req = http.request options, (res) ->
res.setEncoding 'utf8'
res.on 'data', (chunk) ->
response_body += chunk
res.on 'end', () ->
callback response_body.substring(0,4) == 'true'
req.setHeader 'Content-Length', request_body.length
req.setHeader 'Content-Type', 'application/x-www-form-urlencoded'
req.write request_body, 'utf8'
req.end()
Thank you :)
+1 to #florian for the very helpful answer. For posterity, I thought I'd provide some information about how to verify what your captcha request looks like to help you make sure that the appropriate headers and parameters are being specified.
If you are on a Mac or a Linux machine or have access to one of these locally, you can use the netcat command to setup a quick server. I guess there are netcat windows ports but I have no experience with them.
nc -l 8100
This command creates a TCP socket listening on pot 8100 and will wait for a connection. You then can change the captcha verify URL from http://www.google.com/recaptcha/... in your server code to be http://localhost:8100/. When your code makes the POST to the verify URL you should see your request outputted to the scree by netcat:
POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 277
Host: localhost:8100
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1 (java 1.5)
privatekey=XXX&remoteip=127.0.0.1&challenge=03AHJYYY...&response=some+words
Using this, I was able to see that my private-key was corrupted.

Resources