https://github.com/francescozoccheddu/swjas
Simple WSGI JSON API Server
https://github.com/francescozoccheddu/swjas
json python server wsgi wsgi-application
Last synced: 5 months ago
JSON representation
Simple WSGI JSON API Server
- Host: GitHub
- URL: https://github.com/francescozoccheddu/swjas
- Owner: francescozoccheddu
- License: mit
- Archived: true
- Created: 2019-07-09T01:27:17.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2024-02-10T20:02:53.000Z (over 2 years ago)
- Last Synced: 2025-09-11T07:31:31.791Z (9 months ago)
- Topics: json, python, server, wsgi, wsgi-application
- Language: Python
- Homepage:
- Size: 37.1 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# swjas
**Simple WSGI JSON API Server**
### Installation
`pip install swjas`
## Core
### Usage
Call `core.makeApplication` to create your WSGI application.
- **`makeApplication`**
- **Parameters**
- `route` : *Iterable*
Iterable of `(path, handler)` tuples or lists
- `path` : `str`
Mapped URL path.
Do not include scheme, authority, query or fragment.
- `handler` : *Callable*
Object (or function) to call to handle incoming POST requests to `path`.
- **Parameters**
- `data` : `dict`, `list`, `int`, `float`, `string`, `bool` or `None`
Deserialied JSON request body if any, otherwhise `None`
- **Returns**
JSON serializable object or `None`.
(Note: `datetime.datetime` objects have built-in serialization support)
(Note: Custom objects can be serialized if they provide a `_json` property that returns a JSON serializable object)
- `allowEmptyRequestBody` : `bool` (`True` by default)
In the case of empty or space request body, if `True` handler argument will be `None`, otherwise a `BadRequestException` will be raised.
(Note: if `True` empty or space request body will not be distinguishable from `null` body)
- **Returns**
WSGI application.
### Examples
Serve using `wsgi_ref`
```python
from wsgiref.simple_server import make_server
from swjas.core import makeApplication
# Services
def authentication(data):
authenticated = data["username"] == "Francesco" and data["password"] == "12345"
return {
"authenticated": authenticated
}
def intDivision(data):
if data["b"] == 0:
return {
"error": "Division by zero"
}
return {
"quotient": data["a"] // data["b"],
"remainder": data["a"] % data["b"]
}
# Server
if __name__ == "__main__":
routes = [
('auth', authentication),
('idiv', intDivision)
]
server = make_server("localhost", 8000, makeApplication(routes))
server.serve_forever()
```
Serve using `waitress`
```python
import waitress
from swjas.core import makeApplication
import random, string
# Services
def generateRandomID(data):
id = ""
for _ in range(data["length"]):
id += random.choice(string.ascii_letters)
return {
"id": id
}
# Server
if __name__ == "__main__":
routes = [
('genid', generateRandomID)
]
waitress.serve(makeApplication(routes), listen='*:8000')
```
Define custom JSON serializable objects
```python
class Point:
def __init__(x, y):
self.x = x
self.y = y
@property
def _json(self):
return [self.x, self.y]
```
## Clean
Validate and clean the request body.
### Usage
Decorate your handlers with the `clean.clean` decorator.
- **`clean`**
- **Parameters**
- `field` : `Field`
Expected request body scheme.
- **Returns**
Validated and cleaned request body.
- **Raises**
- `exceptions.BadRequestException`
If the request body does not match the provided scheme.
- **`Field`**
- **Constructor**
- **Parameters**
- `missing` : `Do` or `Default`
What to do when this field is missing.
- `error` : `Do` or `Default`
What to do when this field is invalid.
- **Methods**
- `clean` *(abstract)*
Validate and clean the field.
Derived fields must implement it.
- **Parameters**
- `value`
Deserialized request body value to clean.
- **Returns**
Cleaned value.
- **Raises**
- `FieldException`
If the value is invalid.
- `cleanAndAdd`
Validate and clean the field.
- **Parameters**
- `present` : `bool`
Whether the field is present or not.
- `value`
Deserialized request body value to clean.
- `add` : *Callable*
Object (or function) to call if the field is cleaned.
- **Parameters**
- `value`
Cleaned value.
### Examples
Simple cleaning
```python
from swjas.clean import clean, Default, IntField, StringField, FloatField, DictField, ListField
@clean(DictField({
"a": IntField(min=1),
"b": IntField(min=1)
}))
def sumPositiveIntegers(data):
return {
"sum": data["a"] + data["b"]
}
@clean(ListField(minLength=1, fields=FloatField()))
def floatAverage(data):
return sum(data) / len(data)
@clean(DictField({
"username": StringField(minLength=5, maxLength=20, regex=r"[A-Za-z0-9]+"),
"password": StringField(minLength=8, maxLength=16, regex=r"[A-Za-z0-9]+"),
"age": IntField(min=18, max=150, missing=Default(None)),
"fullName": StringField(minLength=1, maxLength=128, missing=Default(None))
}))
def printMe(data):
print(data)
return {
"success": True
}
```
Custom field
```python
from swjas.clean import TypeField, FieldException, Do
class EvenNumberField(TypeField):
def __init__(self, missing=Do.RAISE, error=Do.RAISE):
super().__init__(int, missing=missing, error=error)
def clean(self, value):
value = super().clean(value)
if value % 2 != 0:
raise FieldException("Odd number")
return value
```
## Exceptions
### Usage
Any uncatched `exceptions.HttpException` will be serialized and added to the response body and will set the corresponding Http response status.
Any `exception.PrintableException` cause set with the `raise ... from` syntax will be added to the response body too.
### Examples
Raise an `HttpException`
```python
from swjas.exceptions import AuthorizationException, NotFoundException
if not authenticated:
raise AuthorizationException(message="Requested object requires authentication")
elif not exists:
raise NotFoundException(message="Requested object does not exist")
```
Raise an `HttpException` with cause
```python
try:
validateRequest(data)
except Exception as e:
from swjas.exceptions import BadRequestException
raise BadRequestException(message="Error while validating the request") from e
```
Create an `HttpException` on the fly
```python
from swjas.exceptions import HttpException
raise HttpException.build(410)
```
Define an `HttpException`
```python
from swjas.exceptions import HttpException
class GoneException(HttpException):
statusCode = 410
```
## Client
Send requests.
### Usage
Run `python -m swjas.client -h` from command line or call `client.request` or `client.service` functions.
### Examples
Send a request providing JSON body via command line and save result to JSON file
```
>>> python -m swjas.client //localhost:8080/signup --outputstatus --indent 4 > response.json
{
"username": "Francesco",
"password": "12345"
}
^Z
```
Send a request providing JSON body via file and print result to console
```
>>> python -m swjas.client //localhost:8080/signup --if request.json
{
"result": "success"
}
```
Send a request via script
```python
from swjas.client import HttpException, RequestException, request
data = {
"username": "Francesco",
"password": "12345"
}
try:
res = request("//localhost:8080/signup", data)
except HttpException as e:
print(f"{e.statusCode}: {e.statusMessage}\n{e.responseBody}")
except RequestException as e:
print(f"Request failed: {e}")
```