feat: web, websocket 어댑터 추가 구현
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# Web patch: the client contract
|
||||
|
||||
Two patch formats, and they are not interchangeable.
|
||||
|
||||
## Choosing one
|
||||
|
||||
A route accepts exactly one, and sending the other is a 415 rather than a best-effort guess. The
|
||||
reason is that a mismatch is silent in the worst direction:
|
||||
|
||||
- `{"a": null}` as a **merge patch** deletes `a`. As a **JSON Patch** it is not a patch at all — it
|
||||
is an object where an array was required.
|
||||
- A JSON Patch array read as a merge patch is a document whose fields are array indices. It merges
|
||||
nothing and reports success.
|
||||
|
||||
| | Merge patch | JSON Patch |
|
||||
| --- | --- | --- |
|
||||
| Media type | `application/merge-patch+json` | `application/json-patch+json` |
|
||||
| RFC | 7396 | 6902 |
|
||||
| Shape | A partial document | An ordered array of operations |
|
||||
| Delete | `"field": null` | `{"op":"remove","path":"/field"}` |
|
||||
| Arrays | Replaced whole | Addressable per index |
|
||||
| Preconditions | `If-Match` only | `If-Match`, plus per-field `test` |
|
||||
|
||||
Use merge patch for "change these fields". Use JSON Patch when you need array element edits or a
|
||||
precondition on one field that `If-Match` cannot express.
|
||||
|
||||
## What is refused
|
||||
|
||||
**Fields outside the allowlist.** The server declares which fields a merge patch may modify and
|
||||
which pointers a JSON Patch may address. Anything else is refused, and *every* refused path is
|
||||
named — you will not discover them one round trip at a time.
|
||||
|
||||
The allowlist is checked **before** anything is applied. A refusal tells you nothing about the
|
||||
current values, which is deliberate: deciding afterwards whether a refused field "actually changed
|
||||
anything" would answer a question about data you may not read.
|
||||
|
||||
**Pointer permission does not travel upwards.** Permission on `/profile` covers
|
||||
`/profile/displayName`; permission on `/profile/displayName` does not cover `/profile`, because
|
||||
replacing the parent deletes every sibling.
|
||||
|
||||
**A `move` needs permission on both ends.** Checking only the destination would let you relocate
|
||||
data out of a field you may not touch.
|
||||
|
||||
**Limits.** At most 100 operations, pointer depth at most 16, merge-patch nesting depth at most 32.
|
||||
The first two multiply — each operation walks its pointer over a document the server deep-copied
|
||||
first.
|
||||
|
||||
## Atomicity
|
||||
|
||||
A JSON Patch document applies entirely or not at all. Every operation runs against a working copy;
|
||||
the resource is not touched until all of them have succeeded and the result has passed full
|
||||
validation.
|
||||
|
||||
This is what makes `test` useful. A client putting a `test` first is relying on nothing after it
|
||||
having happened when the test fails, and that reliance holds:
|
||||
|
||||
```json
|
||||
[
|
||||
{"op": "test", "path": "/version", "value": 4},
|
||||
{"op": "replace", "path": "/displayName", "value": "new"}
|
||||
]
|
||||
```
|
||||
|
||||
A failed `test` is **409**, not 400: the document was well-formed and permitted, and the resource
|
||||
simply was not in the state you expected. Re-read and retry. The response names the pointer that
|
||||
failed and does not return the server's value — returning it would make `test` a read primitive for
|
||||
fields you may not read.
|
||||
|
||||
## Validation
|
||||
|
||||
The patched result is validated as a whole document, not field by field. A patch whose individual
|
||||
fields are all valid can produce an object that is not — two fields that must agree, a state
|
||||
transition that is not allowed — and validating only what changed sees none of it.
|
||||
|
||||
## Preconditions
|
||||
|
||||
Patch routes require `If-Match`. A partial update against a resource that moved underneath you
|
||||
applies your changes to a version you never saw.
|
||||
Reference in New Issue
Block a user