Repository: marshmallow-code/webargs

Issue:
bug: webargs 5.0 introduces incompatibility with Flask < 1.0
https://github.com/marshmallow-code/webargs/blob/5.0.0/webargs/flaskparser.py#L63

`_get_data_for_json` is only available since Flask >= 1.0

for Flask < 1.0, there is an error as follows:

```
  File "/usr/local/lib/python2.7/site-packages/webargs/flaskparser.py", line 63, in parse_json
    data = req._get_data_for_json(cache=True)
  File "/usr/local/lib/python2.7/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
AttributeError: 'Request' object has no attribute '_get_data_for_json'
```

I had to downgrade webargs to 4.4.1 to get it work.

So you need to update this framework requirement https://github.com/marshmallow-code/webargs/blob/dev/setup.py#L11 or update the code for the backward compatibility.

IMHO, using `_get_data_for_json` should be avoided because it's considered private and can be changed/removed anytime.


Discussion:
Thanks for reporting.

I don't fully understand the reason @sloria did it this way.

            # We decode the json manually here instead of
            # using req.get_json() so that we can handle
            # JSONDecodeErrors consistently

Apparently, catching `BadRequest` (which is both flask<1.0 and flask 1.0) was not satisfying.

I wouldn't mind dropping flask < 1.0 if we had to since flask 1 has been out for a while and from my experience, there wasn't much trouble migrating.

Regarding the use of a protected method, I suppose we could call

    self.get_data(cache=cache)

Perhaps we could even add a bit of code from https://github.com/pallets/flask/blob/0.12-maintenance/flask/wrappers.py#L21 to preserve flask 0.x compatibility.
> I don't fully understand the reason @sloria did it this way.

```python
            # We decode the json manually here instead of
            # using req.get_json() so that we can handle
            # JSONDecodeErrors consistently
            data = req._get_data_for_json(cache=True)
```

The reason it is done this way is so that a `JSONDecodeError` would be raised and we could therefore check `e.doc` and pass the exception to `handle_invalid_json_error` (rather than passing a `BadRequest`). This makes the API consistent with the behavior of the other parsers.

```python
            try:
                self._cache["json"] = json_data = core.parse_json(data)
            except json.JSONDecodeError as e:
                if e.doc == "":
                    return core.missing
                else:
                    return self.handle_invalid_json_error(e, req)
```

> Regarding the use of a protected method, I suppose we could call `self.get_data(cache=cache)`

Yeah, this seems like the way to go. Didn't see that method before.

I'm up for dropping Flask 1.0, but that's a breaking change for a major release. For now, let's use `get_data`, which has existed since werkzeug 0.9, which is 5.5 years old.



Resolving patch:
diff --git a/webargs/flaskparser.py b/webargs/flaskparser.py
--- a/webargs/flaskparser.py
+++ b/webargs/flaskparser.py
@@ -60,7 +60,7 @@ def parse_json(self, req, name, field):
             # We decode the json manually here instead of
             # using req.get_json() so that we can handle
             # JSONDecodeErrors consistently
-            data = req._get_data_for_json(cache=True)
+            data = req.get_data(cache=True)
             try:
                 self._cache["json"] = json_data = core.parse_json(data)
             except json.JSONDecodeError as e:


Test patch: