What is the best way to ignore unwanted fields in a JSON payload from PUT / PATCH using Golang?

I have a situation where people consuming our API will need to partially update their resource. I understand that HTTP clearly indicates that this is a PATCH operation, although people on our side are used to send a PUT request for this and how the old code is generated.

For example, imagine a simple structure like this:

type Person struct { Name string Age int Address string } 

In the POST request, I provided a payload with all three values ​​(name, age, address) and checked them accordingly on my Golang server. Plain.

In the PUT / PATCH request, we know that, for example, a name never changes. But I will say that I would like to change age , then I would just send a JSON payload containing the new age :

 PUT /person/1 {age:30} 

Now to my real question: What is the best practice to prevent the use / update of name intentionally or unintentionally changed if a consumer of our API sends a JSON payload containing the name field?

Example:

 PUT /person/1 {name:"New Name", age:35} 

Possible solutions that I thought about, but they really don't like them:

  • In my validator method validator I will either forcefully remove the unnecessary name field OR respond with an error message that name not resolved.

  • Create a DTO object / structure that will be pretty much an extension of my Person structure and then unmarshall my JSON payload into it, like

    type PersonPut struct { Age int Address string }

In my opinion, this will add extra code and logic to abstract the problem, however I do not see another elegant solution.

Honestly, I do not like these two approaches, and I would like to know if you encountered the same problem and how you solved it.

Thanks!

+5
source share
5 answers

If the name cannot be spelled, it is not valid to provide it for any update request. I would decline the request if the name was. If I wanted to be more lenient, I could only consider rejecting the request if the name is different from the current name.

I would not ignore a name other than the current name.

+1
source

The first solution you brought is a good one. Some well-known structures are used to implement similar logic.

As an example, versions of latests Rails come with a built-in solution that allows users to add additional data to the request, causing the server to update invalid fields in the database. This is a kind of whitelist implemented by the ActionController::Parameters class.

Suppose we have a controller class as shown below. For the purpose of this explanation, it contains two update actions. But you will not see it in real code.

 class PeopleController < ActionController::Base # 1st version - Unsafe, it will rise an exception. Don't do it def update person = current_account.people.find(params[:id]) person.update!(params[:person]) redirect_to person end # 2nd version - Updates only permitted parameters def update person = current_account.people.find(params[:id]) person.update!(person_params) # call to person_params method redirect_to person end private def person_params params.require(:person).permit(:name, :age) end end 

Since the second version only allows valid values, it blocks the user from changing the payload and sends a JSON containing the new password value:

 { name: "acme", age: 25, password: 'account-hacked' } 

For more information, see the Rails docs: Overview of Action Controller and ActionController :: Parameters

+1
source

This can be solved by first decrypting the JSON body on map[string]json.RawMessage . The json.RawMessage type json.RawMessage useful for delaying actual decoding. After that, you can use the whitelist on the map[string]json.RawMessage , ignoring unwanted properties and only decrypting the json.RawMessage properties that we want to keep.

The process of decoding a white JSON body into a structure can be automated using the reflect package; An example implementation can be found here .

+1
source

I don't own Golang, but I think a good strategy will turn your name field into a read-only field.

For example, in a strictly object-oriented language like Java / .NET / C ++, you can simply provide Getter, but not Setter.

Perhaps there is some kind of access configuration for the Golang, like Ruby ....

If it is read-only, it should not worry about getting a fallback value, it should just ignore it. But then again, not sure if the Golang is supported.

0
source

I think the clean way is to put this logic in a PATCH handler. There must be some logic that will only update the fields that you want. This is easier if you unpack the map[string]string and only iterate over the fields you want to update. In addition, you can decode json to a card, delete all fields that you do not want to update, transcode to json and then decode to your structure.

0
source

Source: https://habr.com/ru/post/1210776/


All Articles