1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-02-23 13:02:28 +00:00
deprecated-gotham-restful/src/lib.rs

353 lines
9.9 KiB
Rust
Raw Normal View History

2020-04-25 17:01:16 +02:00
#![allow(clippy::tabs_in_doc_comments)]
2020-05-03 18:21:50 +02:00
#![warn(missing_debug_implementations, rust_2018_idioms)]
#![deny(intra_doc_link_resolution_failure)]
2019-10-14 00:59:02 +02:00
/*!
2020-05-04 21:34:20 +02:00
This crate is an extension to the popular [gotham web framework][gotham] for Rust. It allows you to
create resources with assigned methods that aim to be a more convenient way of creating handlers
2020-05-08 15:10:37 +02:00
for requests.
# Design Goals
This is an opinionated framework on top of [gotham]. Unless your web server handles mostly JSON as
request/response bodies and does that in a RESTful way, this framework is probably a bad fit for
your application. The ultimate goal of gotham-restful is to provide a way to write a RESTful
web server in Rust as convenient as possible with the least amount of boilerplate neccessary.
# Methods
Assuming you assign `/foobar` to your resource, you can implement the following methods:
2020-05-04 21:34:20 +02:00
| Method Name | Required Arguments | HTTP Verb | HTTP Path |
| ----------- | ------------------ | --------- | ----------- |
| read_all | | GET | /foobar |
| read | id | GET | /foobar/:id |
| search | query | GET | /foobar/search |
| create | body | POST | /foobar |
| change_all | body | PUT | /foobar |
| change | id, body | PUT | /foobar/:id |
| remove_all | | DELETE | /foobar |
| remove | id | DELETE | /foobar/:id |
2019-10-14 00:59:02 +02:00
2020-05-04 21:34:20 +02:00
Each of those methods has a macro that creates the neccessary boilerplate for the Resource. A
simple example could look like this:
2019-10-14 00:59:02 +02:00
```rust,no_run
# #[macro_use] extern crate gotham_restful_derive;
2020-05-04 21:34:20 +02:00
# use gotham::router::builder::*;
# use gotham_restful::*;
2019-10-14 00:59:02 +02:00
# use serde::{Deserialize, Serialize};
2020-05-04 21:34:20 +02:00
/// Our RESTful resource.
2019-10-14 00:59:02 +02:00
#[derive(Resource)]
2020-05-04 21:34:20 +02:00
#[resource(read)]
struct FooResource;
2019-10-14 00:59:02 +02:00
2020-05-04 21:34:20 +02:00
/// The return type of the foo read method.
#[derive(Serialize)]
2020-05-16 01:01:20 +02:00
# #[cfg_attr(feature = "openapi", derive(OpenapiType))]
2020-05-04 21:34:20 +02:00
struct Foo {
id: u64
2019-10-14 00:59:02 +02:00
}
2020-05-04 21:34:20 +02:00
/// The foo read method handler.
#[read(FooResource)]
fn read(id: u64) -> Success<Foo> {
Foo { id }.into()
2019-10-14 00:59:02 +02:00
}
2020-05-04 21:34:20 +02:00
# fn main() {
# gotham::start("127.0.0.1:8080", build_simple_router(|route| {
# route.resource::<FooResource>("foo");
# }));
# }
2019-10-14 00:59:02 +02:00
```
2020-05-04 21:34:20 +02:00
# Arguments
Some methods require arguments. Those should be
* **id** Should be a deserializable json-primitive like `i64` or `String`.
* **body** Should be any deserializable object, or any type implementing [`RequestBody`].
* **query** Should be any deserializable object whose variables are json-primitives. It will
however not be parsed from json, but from HTTP GET parameters like in `search?id=1`. The
type needs to implement [`QueryStringExtractor`].
Additionally, non-async handlers may take a reference to gotham's [`State`]. If you need to
have an async handler (that is, the function that the method macro is invoked on is declared
as `async fn`), consider returning the boxed future instead. Since [`State`] does not implement
`Sync` there is unfortunately no more convenient way.
# Uploads and Downloads
By default, every request body is parsed from json, and every respone is converted to json using
[serde_json]. However, you may also use raw bodies. This is an example where the request body
is simply returned as the response again, no json parsing involved:
2019-10-20 14:49:53 +00:00
```rust,no_run
# #[macro_use] extern crate gotham_restful_derive;
2020-05-04 21:34:20 +02:00
# use gotham::router::builder::*;
# use gotham_restful::*;
2019-10-20 14:49:53 +00:00
# use serde::{Deserialize, Serialize};
#[derive(Resource)]
2020-05-04 21:34:20 +02:00
#[resource(create)]
2019-10-20 14:49:53 +00:00
struct ImageResource;
#[derive(FromBody, RequestBody)]
#[supported_types(mime::IMAGE_GIF, mime::IMAGE_JPEG, mime::IMAGE_PNG)]
2020-04-19 22:26:29 +02:00
struct RawImage {
content: Vec<u8>,
content_type: Mime
}
2019-10-20 14:49:53 +00:00
2020-05-04 21:34:20 +02:00
#[create(ImageResource)]
fn create(body : RawImage) -> Raw<Vec<u8>> {
2020-04-19 22:26:29 +02:00
Raw::new(body.content, body.content_type)
2019-10-20 14:49:53 +00:00
}
# fn main() {
# gotham::start("127.0.0.1:8080", build_simple_router(|route| {
2020-04-05 22:18:31 +02:00
# route.resource::<ImageResource>("image");
2019-10-20 14:49:53 +00:00
# }));
# }
```
2020-05-04 21:34:20 +02:00
# Features
To make life easier for common use-cases, this create offers a few features that might be helpful
when you implement your web server.
## Authentication Feature
In order to enable authentication support, enable the `auth` feature gate. This allows you to
register a middleware that can automatically check for the existence of an JWT authentication
token. Besides being supported by the method macros, it supports to lookup the required JWT secret
with the JWT data, hence you can use several JWT secrets and decide on the fly which secret to use.
None of this is currently supported by gotham's own JWT middleware.
A simple example that uses only a single secret could look like this:
```rust,no_run
2020-05-16 01:03:17 +02:00
# #[macro_use] extern crate gotham_restful_derive;
2020-05-16 01:01:20 +02:00
# #[cfg(feature = "auth")]
# mod auth_feature_enabled {
2020-05-04 21:34:20 +02:00
# use gotham::{router::builder::*, pipeline::{new_pipeline, single::single_pipeline}, state::State};
# use gotham_restful::*;
# use serde::{Deserialize, Serialize};
#[derive(Resource)]
#[resource(read)]
struct SecretResource;
#[derive(Serialize)]
2020-05-16 01:01:20 +02:00
# #[cfg_attr(feature = "openapi", derive(OpenapiType))]
2020-05-04 21:34:20 +02:00
struct Secret {
id: u64,
intended_for: String
}
#[derive(Deserialize, Clone)]
struct AuthData {
sub: String,
exp: u64
}
2019-10-14 00:59:02 +02:00
2020-05-04 21:34:20 +02:00
#[read(SecretResource)]
fn read(auth: AuthStatus<AuthData>, id: u64) -> AuthSuccess<Secret> {
let intended_for = auth.ok()?.sub;
Ok(Secret { id, intended_for })
}
fn main() {
let auth: AuthMiddleware<AuthData, _> = AuthMiddleware::new(
AuthSource::AuthorizationHeader,
AuthValidation::default(),
StaticAuthHandler::from_array(b"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc")
);
let (chain, pipelines) = single_pipeline(new_pipeline().add(auth).build());
gotham::start("127.0.0.1:8080", build_router(chain, pipelines, |route| {
route.resource::<SecretResource>("secret");
}));
}
2020-05-16 01:01:20 +02:00
# }
2020-05-04 21:34:20 +02:00
```
## Database Feature
The database feature allows an easy integration of [diesel] into your handler functions. Please
note however that due to the way gotham's diesel middleware implementation, it is not possible
to run async code while holding a database connection. If you need to combine async and database,
you'll need to borrow the connection from the [`State`] yourself and return a boxed future.
A simple non-async example could look like this:
```rust,no_run
2020-05-16 01:01:20 +02:00
# #[cfg(feature = "database")]
# mod database_feature_enabled {
2020-05-04 21:34:20 +02:00
# #[macro_use] extern crate diesel;
# #[macro_use] extern crate gotham_restful_derive;
# use diesel::{table, PgConnection, QueryResult, RunQueryDsl};
# use gotham::{router::builder::*, pipeline::{new_pipeline, single::single_pipeline}, state::State};
# use gotham_middleware_diesel::DieselMiddleware;
# use gotham_restful::*;
# use serde::{Deserialize, Serialize};
# use std::env;
# table! {
# foo (id) {
# id -> Int8,
# value -> Text,
# }
# }
#[derive(Resource)]
#[resource(read_all)]
struct FooResource;
#[derive(Queryable, Serialize)]
2020-05-16 01:01:20 +02:00
# #[cfg_attr(feature = "openapi", derive(OpenapiType))]
2020-05-04 21:34:20 +02:00
struct Foo {
id: i64,
value: String
}
#[read_all(FooResource)]
fn read_all(conn: &PgConnection) -> QueryResult<Vec<Foo>> {
foo::table.load(conn)
}
type Repo = gotham_middleware_diesel::Repo<PgConnection>;
fn main() {
let repo = Repo::new(&env::var("DATABASE_URL").unwrap());
let diesel = DieselMiddleware::new(repo);
let (chain, pipelines) = single_pipeline(new_pipeline().add(diesel).build());
gotham::start("127.0.0.1:8080", build_router(chain, pipelines, |route| {
route.resource::<FooResource>("foo");
}));
}
2020-05-16 01:01:20 +02:00
# }
2020-05-04 21:34:20 +02:00
```
2019-10-20 17:18:51 +02:00
2020-05-04 21:34:20 +02:00
# Examples
2019-10-20 17:18:51 +02:00
2020-05-04 21:34:20 +02:00
There is a lack of good examples, but there is currently a collection of code in the [example]
directory, that might help you. Any help writing more examples is highly appreciated.
2019-10-20 17:18:51 +02:00
2019-10-14 00:59:02 +02:00
# License
Licensed under your option of:
- [Apache License Version 2.0](https://gitlab.com/msrd0/gotham-restful/blob/master/LICENSE-Apache)
- [Eclipse Public License Version 2.0](https://gitlab.com/msrd0/gotham-restful/blob/master/LICENSE-EPL)
2019-10-14 00:59:02 +02:00
2020-05-04 21:34:20 +02:00
[diesel]: https://diesel.rs/
[example]: https://gitlab.com/msrd0/gotham-restful/tree/master/example
[gotham]: https://gotham.rs/
[serde_json]: https://github.com/serde-rs/json#serde-json----
[`QueryStringExtractor`]: ../gotham/extractor/trait.QueryStringExtractor.html
[`RequestBody`]: trait.RequestBody.html
[`State`]: ../gotham/state/struct.State.html
2019-10-14 00:59:02 +02:00
*/
2019-10-14 02:37:50 +02:00
// weird proc macro issue
extern crate self as gotham_restful;
2019-09-27 16:36:38 +02:00
#[macro_use] extern crate gotham_derive;
#[macro_use] extern crate log;
2019-09-27 16:36:38 +02:00
#[macro_use] extern crate serde;
2019-09-26 17:24:40 +02:00
2019-10-20 14:49:53 +00:00
#[doc(no_inline)]
2020-04-15 23:20:41 +02:00
pub use gotham;
#[doc(no_inline)]
pub use gotham::{
hyper::{header::HeaderName, StatusCode},
state::{FromState, State}
};
2019-10-20 14:49:53 +00:00
#[doc(no_inline)]
pub use mime::Mime;
2019-09-26 17:24:40 +02:00
2019-10-06 15:03:30 +02:00
pub use gotham_restful_derive::*;
2019-10-13 23:19:34 +02:00
2019-10-06 15:03:30 +02:00
/// Not public API
#[doc(hidden)]
pub mod export
{
2020-04-15 20:55:25 +02:00
pub use futures_util::future::FutureExt;
2020-01-14 23:16:31 +01:00
pub use serde_json;
#[cfg(feature = "database")]
pub use gotham_middleware_diesel::Repo;
2020-01-14 23:16:31 +01:00
2019-10-06 15:03:30 +02:00
#[cfg(feature = "openapi")]
pub use indexmap::IndexMap;
#[cfg(feature = "openapi")]
2019-10-13 23:19:34 +02:00
pub use openapiv3 as openapi;
2019-10-06 15:03:30 +02:00
}
#[cfg(feature = "auth")]
mod auth;
#[cfg(feature = "auth")]
pub use auth::{
AuthHandler,
AuthMiddleware,
AuthSource,
AuthStatus,
AuthValidation,
StaticAuthHandler
};
2020-05-13 19:10:53 +02:00
#[cfg(feature = "cors")]
mod cors;
#[cfg(feature = "cors")]
pub use cors::{
handle_cors,
CorsConfig,
2020-05-14 23:30:59 +02:00
CorsRoute,
2020-05-13 19:10:53 +02:00
Origin
};
pub mod matcher;
2019-09-29 21:15:22 +02:00
#[cfg(feature = "openapi")]
2019-10-14 00:59:02 +02:00
mod openapi;
2019-09-30 18:18:10 +02:00
#[cfg(feature = "openapi")]
2019-09-30 20:58:15 +02:00
pub use openapi::{
2020-05-03 19:17:55 +02:00
builder::OpenapiInfo,
router::GetOpenapi,
2019-10-01 15:33:05 +02:00
types::{OpenapiSchema, OpenapiType}
2019-09-30 20:58:15 +02:00
};
2019-09-29 21:15:22 +02:00
2019-09-26 17:24:40 +02:00
mod resource;
pub use resource::{
Resource,
2020-04-06 16:20:08 +00:00
ResourceMethod,
2019-09-28 13:38:08 +02:00
ResourceReadAll,
ResourceRead,
2019-10-13 17:43:42 +02:00
ResourceSearch,
2019-09-28 13:38:08 +02:00
ResourceCreate,
ResourceChangeAll,
ResourceChange,
ResourceRemoveAll,
ResourceRemove
2019-09-26 17:24:40 +02:00
};
mod response;
pub use response::Response;
2019-09-26 17:24:40 +02:00
mod result;
2019-10-05 14:50:05 +02:00
pub use result::{
AuthError,
AuthError::Forbidden,
AuthErrorOrOther,
2020-01-25 15:59:37 +01:00
AuthResult,
AuthSuccess,
IntoResponseError,
2019-10-05 14:50:05 +02:00
NoContent,
2019-10-20 14:49:53 +00:00
Raw,
2019-10-05 14:50:05 +02:00
ResourceResult,
Success
};
2019-09-26 17:24:40 +02:00
mod routing;
2019-09-26 17:42:28 +02:00
pub use routing::{DrawResources, DrawResourceRoutes};
2019-09-30 18:18:10 +02:00
#[cfg(feature = "openapi")]
pub use routing::WithOpenapi;
2019-09-30 20:58:15 +02:00
2019-10-14 00:59:02 +02:00
mod types;
2019-10-20 14:49:53 +00:00
pub use types::*;