-
Notifications
You must be signed in to change notification settings - Fork 1
Server
Using Razor is extremely simple, as mentioned in the README.
Building a basic Razor server is as simple as breathing:
from razor.server import Application
from razor.server import TextResponse
app = Application(__name__)
@app.route("/index/")
async def index():
return TextResponse("hello world")
if __name__ == "__main__":
app.run()
You can also use the command line of uvicorn to start the project:
$ uvicorn setup:app --host 127.0.0.1 --port 5200
Razor uses port 5200 by default.
At its current stage, Razor uses http-route to build its routing system.
Below are common examples of routes. For more details, you can refer to the http-route documentation:
from razor.server import Application
from razor.server import TextResponse
app = Application(__name__)
@app.route("/index/")
async def index():
return TextResponse("hello world")
@app.route("/book/{book_id:int}/")
async def book(book_id):
return TextResponse(f"book_id : {book_id}")
@app.route(r"/page/{page:\d+}/", methods=["GET", "POST"])
async def page(page):
return TextResponse(f"page : {page}")
if __name__ == "__main__":
app.run()
Razor provides a straightforward way to handle routes for Class-Based-Views:
from razor.server import Application
from razor.server import TextResponse
from razor.server import View
app = Application(__name__)
@app.route("/example/{page}/")
class Example(View):
async def get(self, page):
# default page type is str
return TextResponse(f"example get ! {page}")
if __name__ == "__main__":
app.run()
Razor also offers a routing mapping approach similar to Django:
app.add_routes(
("/index/", index),
("/example/", Example.as_view())
)
Unfortunately, Razor does not currently support alias parameters such as name or alias.
Even switches with a single URL strict match are not supported, but I think this is acceptable at this stage.
Razor's request is inspired by the design of flask 。
You only need to import it and then use it as shown below:
from razor.server import (
request,
Application,
TextResponse
)
app = Application(__name__)
@app.route("/index/")
async def index():
print(request.method)
return TextResponse("index")
if __name__ == "__main__":
app.run()
The request provides a variety of methods:
- attribute
- content
- headers
- cookies
- query
- async method
- body
- text
- form
- files
- json
- data
Additionally, you can also access scope information using the . or [] syntax:
{
'type': 'http',
'asgi': {
'version': '3.0',
'spec_version': '2.3'
},
'http_version': '1.1',
'server': ('127.0.0.1', 5000),
'client': ('127.0.0.1', 51604),
'scheme': 'http',
'method': 'GET',
'root_path': '',
'path': '/index/',
'raw_path': b'/index/',
'query_string': b'k1=v1&k2=v2',
'headers': [
(b'host', b'127.0.0.1:5000'),
(b'user-agent', b'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0'),
(b'accept', b'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8'),
(b'accept-language', b'en-US,en;q=0.5'),
(b'accept-encoding', b'gzip, deflate, br'),
(b'connection', b'keep-alive'),
(b'upgrade-insecure-requests', b'1'),
(b'sec-fetch-dest', b'document'),
(b'sec-fetch-mode', b'navigate'),
(b'sec-fetch-site', b'cross-site')
],
'state': {}}
For example:
request.method
request.server
request.client
request.headers
Here's an example of uploading a file and saving it to disk:
from razor.server import (
request,
Application,
TextResponse
)
app = Application(__name__)
@app.route("/files/", methods=["POST"])
async def index():
files = await request.files()
file = files["avater"]
await file.save(f"./avater/{file.name}")
return TextResponse("save file success")
if __name__ == "__main__":
app.run()
Razor temporarily stores files in memory and writes the file to disk when you call the save method.
If the specified directory for saving the file doesn't exist, Razor will automatically create it.
If you don't provide any path to the save method, the file will be saved in the current directory by default.
Feel free to ask if you have any more questions!
Razor offers several packages for Response:
from razor.server import (
TextResponse,
HtmlResponse,
JsonResponse,
RedirectResponse,
ErrorResponse
)
The following are examples of usage:
return TextResponse("hello world")
return HtmlResponse("<div>hello world</div>")
return JsonResponse({"code": 0})
return RedirectResponse("/doc/")
return ErrorResponse(500)
The handling of response headers and cookies is also very simple:
from razor.server import (
Application,
TextResponse
)
app = Application(__name__)
@app.route("/index/", methods=["POST"])
async def index():
resp = TextResponse("hello world")
resp.cookies["k1"] = "v1"
resp.headers["k1"] = "v1"
return resp
if __name__ == "__main__":
app.run()
Razor provides rich event hook functions:
"startup",
"shutdown",
"after_request",
"before_request",
"exception"
They can be used with app.on_event:
from typing import Type, Optional, Union
from razor.server import (
Application,
Response
)
app = Application(__name__)
@app.on_event("startup")
async def startup() -> None:
print("startup")
@app.on_event("shutdown")
async def shutdown() -> None:
print("shutdown")
@app.on_event("before_request")
async def before_request() -> Optional[Type[Response]]:
print("before request")
@app.on_event("after_request")
async def after_request(resp: Type[Response]) -> Type[Response]:
print("after request")
return resp
@app.on_event("exception")
async def exception_handle(exc: Type[Exception]) -> Union[Type[Exception], Type[Response]]:
print("exception handle")
return exc
The same can be achieved with the quick decorators provided by Razor:
- on_startup
- on_shutdown
- on_before_request
- on_after_request
- on_exception
Example:
@app.on_startup
async def startup() -> None:
print("startup")
Razor reuses uvicorn's logger by default, so you only need to pass in the logger configuration for the Run method of Application.
app.run(log_config=LOGGER_CONF)
log_config can be the path to a Dict or JSON file or yaml file.
Use example:
from razor.server import Application
from razor.server import logger as log
app = Application(__name__)
@app.on_startup
async def startup():
log.info("startup")
if __name__ == "__main__":
app.run()
In addition to the request context, Razor also provides an application context.
from razor.server import current_application
It ensures that you can access the current application instance object at any time.