diff --git a/app/decorators.py b/app/decorators.py
index 47e6d7491aa1d18eaf1255a80a4f590f1dffa257..5fa3d82b13790d798e115cb2aaf74412cec12d4a 100644
--- a/app/decorators.py
+++ b/app/decorators.py
@@ -1,7 +1,9 @@
-from flask import abort, current_app
+from flask import abort, current_app, request
 from flask_login import current_user
 from functools import wraps
 from threading import Thread
+from typing import List, Union
+from werkzeug.exceptions import NotAcceptable
 from app.models import Permission
 
 
@@ -61,3 +63,55 @@ def background(f):
         thread.start()
         return thread
     return wrapped
+
+
+def consumes(mime_type: str, *_mime_types: str):
+    def decorator(f):
+        @wraps(f)
+        def decorated_function(*args, **kwargs):
+            provided = request.mimetype
+            consumeables = {mime_type, *_mime_types}
+            if provided not in consumeables:
+                raise NotAcceptable()
+            return f(*args, **kwargs)
+        return decorated_function
+    return decorator
+
+
+def produces(mime_type: str, *_mime_types: str):
+    def decorator(f):
+        @wraps(f)
+        def decorated_function(*args, **kwargs):
+            accepted = {*request.accept_mimetypes.values()}
+            produceables = {mime_type, *_mime_types}
+            if len(produceables & accepted) == 0:
+                raise NotAcceptable()
+            return f(*args, **kwargs)
+        return decorated_function
+    return decorator
+
+
+def content_negotiation(
+    produces: Union[str, List[str]],
+    consumes: Union[str, List[str]]
+):
+    def decorator(f):
+        @wraps(f)
+        def decorated_function(*args, **kwargs):
+            provided = request.mimetype
+            if isinstance(consumes, str):
+                consumeables = {consumes}
+            else:
+                consumeables = {*consumes}
+            accepted = {*request.accept_mimetypes.values()}
+            if isinstance(produces, str):
+                produceables = {produces}
+            else:
+                produceables = {*produces}
+            if len(produceables & accepted) == 0:
+                raise NotAcceptable()
+            if provided not in consumeables:
+                raise NotAcceptable()
+            return f(*args, **kwargs)
+        return decorated_function
+    return decorator