html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,issue,performed_via_github_app
https://github.com/simonw/datasette/issues/1949#issuecomment-1352411327,https://api.github.com/repos/simonw/datasette/issues/1949,1352411327,IC_kwDOBm6k_c5QnCi_,9599,2022-12-15T00:46:27Z,2022-12-15T00:46:27Z,OWNER,"I got this far:
```diff
diff --git a/datasette/handle_exception.py b/datasette/handle_exception.py
index 8b7e83e3..31d41e00 100644
--- a/datasette/handle_exception.py
+++ b/datasette/handle_exception.py
@@ -54,7 +54,17 @@ def handle_exception(datasette, request, exception):
headers = {}
if datasette.cors:
add_cors_headers(headers)
- if request.path.split(""?"")[0].endswith("".json""):
+ # Return JSON error under certain conditions
+ should_return_json = (
+ # URL ends in .json
+ request.path.split(""?"")[0].endswith("".json"")
+ or
+ # Hints from incoming request headers
+ request.headers.get(""content-type"") == ""application/json""
+ or ""application/json"" in request.headers.get(""accept"", """")
+ )
+ breakpoint()
+ if should_return_json:
return Response.json(info, status=status, headers=headers)
else:
template = datasette.jinja_env.select_template(templates)
diff --git a/tests/test_api_write.py b/tests/test_api_write.py
index f27d143f..982543a6 100644
--- a/tests/test_api_write.py
+++ b/tests/test_api_write.py
@@ -1140,6 +1140,38 @@ async def test_create_table_permissions(
assert data[""errors""] == expected_errors
+@pytest.mark.asyncio
+@pytest.mark.parametrize(
+ ""headers,expect_json"",
+ (
+ ({}, False),
+ ({""Accept"": ""text/html""}, True),
+ ({""Accept"": ""application/json""}, True),
+ ({""Content-Type"": ""application/json""}, True),
+ ({""Accept"": ""application/json, text/plain, */*""}, True),
+ ({""Content-Type"": ""application/json""}, True),
+ ({""accept"": ""application/json, text/plain, */*""}, True),
+ ({""content-type"": ""application/json""}, True),
+ ),
+)
+async def test_permission_errors_html_and_json(ds_write, headers, expect_json):
+ request_headers = {""Authorization"": ""Bearer bad_token""}
+ request_headers.update(headers)
+ response = await ds_write.client.post(
+ ""/data/-/create"",
+ json={},
+ headers=request_headers,
+ )
+ assert response.status_code == 403
+ if expect_json:
+ data = response.json()
+ assert data[""ok""] is False
+ assert data[""errors""] == [""Permission denied""]
+ else:
+ assert response.headers[""Content-Type""] == ""text/html; charset=utf-8""
+ assert ""Permission denied"" in response.text
+
+
@pytest.mark.asyncio
@pytest.mark.parametrize(
""input,expected_rows_after"",
```
Then decided I would punt this until the next milestone.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1493471221,
https://github.com/simonw/datasette/issues/1949#issuecomment-1352378370,https://api.github.com/repos/simonw/datasette/issues/1949,1352378370,IC_kwDOBm6k_c5Qm6gC,9599,2022-12-15T00:02:08Z,2022-12-15T00:04:54Z,OWNER,"I fixed this issue to help research this further:
- https://github.com/simonw/datasette-ripgrep/issues/26
Now this search works:
I wish I had this feature!
- https://github.com/simonw/datasette-ripgrep/issues/24
Looks like I have both `_error()` and `_errors()` functions in there!
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1493471221,