poem-web / poem Goto Github PK
View Code? Open in Web Editor NEWA full-featured and easy-to-use web framework with the Rust programming language.
License: Apache License 2.0
A full-featured and easy-to-use web framework with the Rust programming language.
License: Apache License 2.0
How about put some common attributes on the impl scope?
such as prefix_path
and openapi tag
.
#[OpenApi]
#[oai(prefix_path = "/pet", tag = ApiTags::Pet)]
impl Api {
#[oai(path = "/", method = "post")]
async fn add_pet(&self, pet: Json<Pet>) -> Result<()> {
todo!()
}
// ...
}
Hi @sunli829
Again, this is probably due to my limited knowledge of poem.
Basically, I want to use PgSessionStore
.
Route::new().at(
"query",
post(GraphQL::new(schema))
.get(graphql_playground)
.with(server_session),
)
It throws error the trait bound `impl std::future::Future: SessionStorage` is not satisfied
If I try something like the example
Route::new().at(
"query",
post(GraphQL::new(schema))
.get(graphql_playground),
).with(server_session)
Then I don't know how to turn in back as a Route
(am setting up nested routing).
Curious how you would pass request data (for example, Bearer Token from headers) into an async-graphql endpoint?
Good day @sunli829
I am still testing with poem
, I want to share small observation with dbsession
for consideration or further checking.
For postgres, not sure if it is correct or not but it looks like it will fail if session column is not JSONB. If it is then maybe there should be a note.
Cleanup task should probably be configurable. When we have multiple poem servers running, we probably don't want every server to ping database to do clean up. I am not sure what the best way to approach this as some pros and cons of each setup or we just need to accept the duplicate jobs?
Not very high priority, the value of in session in db looks like this {"id": "\"xxx\""}
, it looks like the library does serialising twice at some points. It doesn't seem to affect how it works, nevertheless it is kind of nice to have correct json presentation.
Look at the following files:
https://github.com/poem-web/poem/blob/master/poem/CHANGELOG.md
https://github.com/poem-web/poem/blob/master/poem-lambda/CHANGELOG.md
https://github.com/poem-web/poem/blob/master/poem-openapi/CHANGELOG.md
The change entries are all from 2010 and not 2021 for some reason. I apologize if I am not understanding something here...
Hi @sunli829, awesome work as always! this library looks as clean as async-graphql
and +1 for the name 😄
Currently it does not support PgSessionStore
, so you have any thought about it?
Like should it be here or which library to use with Pg?
The example looks like this.
`use poem::{handler, route, web::Path, RouteMethod, Server};
#[handler]
fn hello(Path(name): Path) -> String {
format!("hello: {}", name)
}
#[tokio::main]
async fn main() {
let app = route().at("/hello/:name", RouteMethod::new().get(hello));
let server = Server::bind("127.0.0.1:3000").await.unwrap();
server.run(app).await.unwrap();
}but i think like this below is nice.
use poem::{handler, route, web::Path, RouteMethod, Server};
#[handler]
fn hello(Path(name): Path) -> String {
format!("hello: {}", name)
}
#[tokio::main]
async fn main() {
let app = route().get("/hello/:name", hello);
let server = Server::bind("127.0.0.1:3000").await.unwrap();
server.run(app).await.unwrap();
}`
this is easy to understand and write and more friendly to users. I think.
Json(async-graphql::Response) not adding headers that was insert in async-graphql::Response via context (ctx.insert_http_header).
if there are multiple middleware, How to deal with their priority.
Hey there :)
Would it be possible to parse Parameter Descriptions via ///-comments, like with struct fields etc, instead of the 'desc=' notation?
I struggled to find descriptions in the first place...
poem/poem-openapi/tests/operation_param.rs
Line 249 in 7586983
So maybe in this case:
async fn test(&self,
/// desc
v: Query<i32>
) {
or comparable? Or would that be to inconsistent with linebreaks?
ServerSide Cache is a common scenario in web application especially with heavy loaded, it will be good to have a Cache extractor which can use DiskCache or RedisCache as CacheStore. it's quite similar to ServerSession.
Thanks :)
use poem_openapi::{ApiResponse, Object, payload::Json};
#[derive(Object)]
pub struct SuccessResponses<T> {
status: i32,
data: T,
}
#[derive(Object)]
pub struct ErrorMessage {
code: i32,
reason: String,
}
#[derive(ApiResponse)]
pub enum CustomApiResponse<T> {
#[oai(status = 200)]
Ok(Json<SuccessResponses<T>>),
/// Returns when the BadRequest.
#[oai(status = 400)]
BadRequest,
}
this is error message:
error[E0412]: cannot find type `T` in this scope
--> src\main.rs:6:11
|
6 | data: T,
| ^ not found in this scope
error[E0412]: cannot find type `T` in this scope
--> src\main.rs:19:30
|
19 | Ok(Json<SuccessResponses<T>>),
| ^ not found in this scope
error[E0107]: missing generics for struct `SuccessResponses`
--> src\main.rs:4:12
|
4 | pub struct SuccessResponses<T> {
| ^^^^^^^^^^^^^^^^ expected 1 generic argument
|
note: struct defined here, with 1 generic parameter: `T`
--> src\main.rs:4:12
|
4 | pub struct SuccessResponses<T> {
| ^^^^^^^^^^^^^^^^ -
help: add missing generic argument
|
4 | pub struct SuccessResponses<T><T> {
| ~~~~~~~~~~~~~~~~~~~
error[E0107]: missing generics for enum `CustomApiResponse`
--> src\main.rs:17:10
|
17 | pub enum CustomApiResponse<T> {
| ^^^^^^^^^^^^^^^^^ expected 1 generic argument
|
note: enum defined here, with 1 generic parameter: `T`
--> src\main.rs:17:10
|
17 | pub enum CustomApiResponse<T> {
| ^^^^^^^^^^^^^^^^^ -
help: add missing generic argument
|
17 | pub enum CustomApiResponse<T><T> {
| ~~~~~~~~~~~~~~~~~~~~
If a session is authenticated, then the request should be dispatched to the matching handler and if unauthenticated, it should be redirected to a user specified route, ideally a sign in route.
Related project for Actix-Web: https://github.com/realaravinth/actix-auth-middleware
@sunli829 As I have started using Poem since yesterday so currently I'm in leeches mode but as I grow my understanding of poem I will start contributing to the project.
I am developing some middleware and it seems a bit hard to know how req.extensions
looks like.
It would be helpful if I can debug Extensions
so that I can tell how the middleware interacts with each other.
Thanks.
Currently with CookieJar
, each new Cookie
stored requires manual configuration of security-related properties, which is flexible, but there is a lot of duplication of work being done.
For a site, usually, the cookie security policy is kept consistent, and there are fewer scenarios where each key is configured individually.
And currently poem
, cookie middleware is automatically registered after the feature is turned on, so that the user lacks some control over the initialization of the attributes.
I suggest: Cookie middleware, the user supports custom configuration at the start of the framework and can override the default loading of the cookie middleware.
Subsequent Cookie
default to go configured security properties, the use of light and elegant.
Hi, first of all good work with poem and poem-openapi, I'm enjoying a lot using them and I'll probably migrate some projects to it.
Anyway, I'd like to be able to get (and maybe serve) the generated .json
file that contains the open api specification; it would also be nice to be able to deactivate the ui feature entirely.
This is because I prefer redoc and I'd like to inspect/edit the final open api document.
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
let api_service = OpenApiService::new(Api)
.title("Hello World")
.server("http://localhost:3000/api");
// Get open api spec
let openapi_spec = api_service.get_spec();
let listener = TcpListener::bind("127.0.0.1:3000");
poem::Server::new(listener)
.await?
.run(
Route::new()
.nest("/api", api_service)
.nest("/openapi.json", serve_openapi_spec(openapi_spec))
)
.await
}
When the redis server perform a clean reload, the Poem should treat all sessions as invalid and return None with Session struct.
All pages/url return 500 with a message
Response was of incompatible type: "Response type not string compatible." (response was nil)
与rocket 相比有什么优势
I would normally submit this as a bug but I'm not sure if this is an issue with Poem or Firefox.
I have been using the WebSocket Chat example provided in this repository as a reference for a project I'm working on, however I've noticed that there is always an issue connecting to the the WebSocket with Firefox but not Chromium.
WebSocket code from the example provided in this repository should function regardless of web browser.
WebSocket fails to establish connection on Firefox but functions properly on Chromium.
Error from the Firefox console: Firefox can’t establish a connection to the server at ws://127.0.0.1:3000/ws.
1.0.26
94.0
95.0.4638.69 snap
21.04
Can you please add Double-Submit Token CSRF middleware to poem like there is such middlware for Actix-Web https://github.com/edward-shen/actix-csrf
A schema built with a schema builder can be passed on to use async_graphql_poem::{GraphQL, GraphQLSubscription};
The following code
let schema = Schema::build(Query, Mutation, SubscriptionRoot)
.data(pool)
.finish();
let cors = Cors::new().allow_methods([Method::POST, Method::GET, Method::OPTIONS]);
let app = Route::new()
.at(
"/graphql",
get(graphql_playground)
.options(GraphQL::new(schema.clone()))
.post(GraphQL::new(schema.clone())),
)
.at("/ws", get(GraphQLSubscription::new(schema.clone())))
.with(cors);
will trigger the following error
error[E0308]: mismatched types
--> src/main.rs:173:39
|
173 | .options(GraphQL::new(schema.clone()))
| ^^^^^^^^^^^^^^ expected struct `async_graphql::schema::Schema`
|
= note: expected struct `async_graphql::schema::Schema<_, _, _>`
found struct `async_graphql::Schema<Query, Mutation, SubscriptionRoot>`
= note: perhaps two different versions of crate `async_graphql` are being used?
error[E0308]: mismatched types
--> src/main.rs:174:36
|
174 | .post(GraphQL::new(schema.clone())),
| ^^^^^^^^^^^^^^ expected struct `async_graphql::schema::Schema`, f
ound struct `async_graphql::Schema`
|
= note: expected struct `async_graphql::schema::Schema<_, _, _>`
found struct `async_graphql::Schema<Query, Mutation, SubscriptionRoot>`
= note: perhaps two different versions of crate `async_graphql` are being used?
error[E0308]: mismatched types
--> src/main.rs:176:49
|
176 | .at("/ws", get(GraphQLSubscription::new(schema.clone())))
| ^^^^^^^^^^^^^^ expected struct `async_graphql::schem
a::Schema`, found struct `async_graphql::Schema`
|
= note: expected struct `async_graphql::schema::Schema<_, _, _>`
found struct `async_graphql::Schema<Query, Mutation, SubscriptionRoot>`
= note: perhaps two different versions of crate `async_graphql` are being used?
Trying to use directly async_graphql::schema::Schema doesn't work since it's private.
I'm testing out adding subscriptions to a graphql app. So far appart from this minor bump everything is just amazing!
I am not sure it is a common use case but I am making lots of requests (mainly for testing) and each of them is slightly different.
So I am thinking of using RequestBuilder
so that I can share some common headers.. etc...
However whenever I consume it into Request
I cannot use request_builder
any more, it would be helpful if RequestBuilder
is cloneable.
Thanks!
Api1 has been installed in routing under /api1.
I would expect such a specification to be generated :
{
"openapi": "3.0.0",
"info": {
"title": "Oneof",
"version": "1.0"
},
"servers": [
{
"url": "http://localhost:3000/api1"
}
],
"tags": [],
"paths": {
"/api1/create-post": {
"post": {
Extract from the current specification http://127.0.0.1:3000/spec1.json
{
"openapi": "3.0.0",
"info": {
"title": "Oneof",
"version": "1.0"
},
"servers": [
{
"url": "http://localhost:3000/api1"
}
],
"tags": [],
"paths": {
"/create-post": {
"post": {
I created a test repository https://github.com/szagi3891/poem-test
You have to clone and then run with cargo run
A swagger will be available at "http://127.0.0.1:3000/ui1"
Version: poem = "1.0.34", poem-openapi = "1.0.35"
Platform: MacOs
Subsystem:
struct Api1 {}
#[OpenApi]
impl Api1 {
pub fn new() -> Api1 {
Api1 {}
}
#[oai(path = "/create-post", method = "post")]
async fn create_post(
&self,
obj: Json<MyObj>,
) -> CreateBlogResponse {
CreateBlogResponse::Ok(Json(444))
}
}
let api1 = Api1::new();
let api_service1 = OpenApiService::new(api1, "Oneof", "1.0").server("http://localhost:3000/api1");
let ui1 = api_service1.swagger_ui();
let spec1 = api_service1.spec_endpoint();
let api2 = Api2::new();
let api_service2 = OpenApiService::new(api2, "Oneof", "1.0").server("http://localhost:3000/api2");
let ui2 = api_service2.swagger_ui();
let spec2 = api_service2.spec_endpoint();
Server::new(TcpListener::bind("127.0.0.1:3000"))
.run(Route::new()
.nest("/spec1.json", spec1)
.nest("/api1", api_service1)
.nest("/ui1", ui1)
.nest("/spec2.json", spec2)
.nest("/api2", api_service2)
.nest("/ui2", ui2)
)
.await
Hey there,
I would like to use Poem with Mysql, but they seem to be using incompatible bitvec / funty versions. On build I get a error[E0034]: multiple applicable items in scope --> C:\Users\local\.cargo\registry\src\github.com-1ecc6299db9ec823\bitvec-0.19.4\src\field.rs:307:25
.
This problem seems to be known in funty and can apparently be solved by updating all dependencies to funty 2.0.0 (or at least the same version.) Unfortunately it is nested quite deeply in the dependencies.
Example Toml, full build output, and cargo tree output uploaded here: https://gist.github.com/Christoph-AK/366e6c39c24976d5bf48064e658b6670
Can this be solved from this crate?
MySQL Issue: blackbeam/rust-mysql-simple#300
When I use multi poem servers, I saw multi Info logs like this, can`t tell them apart.
2021-11-22T14:04:31.803030Z INFO poem::server: initiate graceful shutdown
2021-11-22T14:04:31.803331Z INFO poem::server: server stopped
struct CorsMiddleware {}
impl CorsMiddleware {
pub fn new() {
todo!()
}
pub fn allow_credentials(self, allow_credentials: bool) -> Self {
todo!()
}
pub fn allow_header<T>(mut self: Self, header: T) -> Self
where
HeaderName: TryFrom<T>,
<HeaderName as TryFrom<K>>::Error: Into<Error>,
{
todo!()
}
pub fn allow_method(mut self: Self, method: T) -> Self
where
Method: TryFrom<T>,
<Method as TryFrom<K>>::Error: Into<Error>,
{
todo!()
}
pub fn allow_origin(mut self: Self, origin: impl Into<String>) -> Self {
todo!()
}
pub fn allow_all_origin(mut self: Self) -> Self {
todo!()
}
pub fn expose_header<T>(mut self: Self, header: T) -> Self
where
HeaderName: TryFrom<T>,
<HeaderName as TryFrom<K>>::Error: Into<Error>,
{
todo!()
}
pub fn max_age(mut self, max_age: usize) {
todo!()
}
}
impl Middleware<E: Endpoint> for CorsMiddleware {
type Output = CorsMiddlewareImpl;
fn transform(&self, ep: E) -> Self::Output {
todo!()
}
}
Would you like poem to support AWS Lambda, or other Serverless framework?
Thank you.
All responses, regardless of how they're derived, will include a description. Specifically, this bit of the spec specifies it as a required field.
No description is included by default, and there were no (obvious) examples of how to add one. I did find out from poking around that simply adding a comment above a response variant will add a description, but this should be called out in the book (or API docs somewhere, though not sure where that'd fit).
Use a response like this:
#[derive(Debug, ApiResponse)]
enum Response {
#[oai(status = 200)]
Ok(
Json<u64>,
),
}
Notice that the OpenAPI document has the response documented like this:
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "integer",
"format": "uint64"
}
}
}
}
}
Some tooling (including the generator I maintain 😬) will fail schema validation of the document when parsing this.
poem = { version = "1.0.24", features = ["multipart", "tempfile"] }
poem-openapi = "1.0.20"
Hi Sunli, I am using poem::RequestBuilder
to build requests for testing, how do I add query params to request?
There should never be a clash in the names of the generated structures
There is currently a possible collision in the generated structure names.
This part was incorrectly generated in the specification, source code:
#[derive(Object, Debug, PartialEq)]
pub struct B {
v4: String,
}
...
#[derive(OneOf, Debug, PartialEq)]
#[oai(property_name = "type")]
pub enum MyObj2 {
A(A),
B(B),
}
...
#[oai(path = "/put2", method = "post")]
async fn index2(&self, obj: Json<MyObj2>) -> Json<MyObj2> {
obj
}
Ps.
By the way, fantastic library. My compliments on a great job :)
Possibility to use Object macro on a structure where one of the fields is Arc
Error message appears:
no function or associated item named register
found for struct Arc<std::string::String>
in the current scope
Structure affected by the compilation error:
#[derive(Object, Debug, PartialEq)]
struct A {
v1: i32,
v2: Arc<String>,
}
I hope to eliminate and decouple the trace log in it, so that users can reference it in the form of middleware, or give a default implementation
In some cases, I want to print a customized log, or reference a minitrace with less overhead for tracking, or add a trace uniformly_ ID and other means to troubleshoot the problem
Is it possible to add a section in docs about the comparison with other web frames, esp. axum, I found they share lots of similarities in syntax, but the comparison can go more deeper and wider besides only the syntax?
Hi Sunli, poem seems pretty good so far, I wonder where should I start looking into for in terms of log, trace, monitor, error monitoring before I want to move poem into some sort of production? Thanks!
"version"
is required by the library or filled in automatically in order to comply with this part of the spec.
Perhaps OpenApiService::new
could required this and title
(also required) as parameters? Though that'd be a breaking change which you may want to avoid. You could also make the builder return a Result
to check for required params at startup, but that is also a breaking change.
The examples should also probably include version, perhaps using a macro to include the Cargo.toml
version (I think this is possible and is probably what I'll do eventually).
"version"
can be omitted (as it is in the examples I followed) which can lead to validation errors in other tools.
Omit the .version()
method in setting up OpenApiService
and observe that the generated document does not contain a version
field under info
.
[dependencies]
poem = { version = "1.0.24", features = ["multipart", "tempfile"] }
poem-openapi = "1.0.20"
Hi Sunli,
I am writing an Endpoint, using impl Endpoint
, I wonder how can I get query params from request?
I am thinking of using Extractor
like this, FromRequest::from_request(&request, &mut body).await?;
I am probably wrong?
Thanks!
Hello,
I'm really enjoying using Poem, but I've got to the point with my project that I need CORS and I can't figure out how it works? Is there an example somewhere for using the CORS middleware?
I'm doing it like this:
.with( Cors::new() .allow_origin("http://localhost:3000") .allow_method(Method::POST), );
But only the last call seems to take effect?
I recently ran into some tricky issues migrating my project from actix-web
to poem
.
Problem description.
In actix-web
middlerware, I can split the task into two steps.
poem
)Step 1 can be implemented in poem, but no good solution has been found for step 2.
Referring to the middleware implementation in example, can use extensions to add a statusX
handler with extractor to extract Result<Data<StateX>>
to handle it by itself,
But I have 50/60 handlers and it is not convenient to add a state to each handler.
And every time I add middleware, I need to add State*
to the handler, which will make the handler very long.
I would like to ask you for some advice.
#[oai(path = "/hello", method = "post")]
async fn index(&self, Json(Req { a,b }): Json<Req>) -> PlainText<String> {
these code compile ok in poem/poem-openapi 1.0.28
but these code compile error in 1.0.36/1.0.37
I think the #[OpenApi]
macro refactor cause this compile error,
Json should not eat a param from in route path, only Query/Path would eat param
Checking example-openapi-hello-world v0.1.0 (/home/w/repos/fork_repos/poem/examples/openapi/hello-world)
error: Invalid param definition.
--> openapi/hello-world/src/main.rs:9:27
|
9 | async fn index(&self, Json(Req { a,b }): Json<Req>) -> PlainText<String> {
use poem::{listener::TcpListener, Route, Server};
use poem_openapi::{param::Query, payload::{PlainText, Json}, OpenApi, OpenApiService};
struct Api;
#[OpenApi]
impl Api {
#[oai(path = "/hello", method = "post")]
async fn index(&self, Json(Req { a,b }): Json<Req>) -> PlainText<String> {
match b {
Some(name) => PlainText(format!("hello, {}!", name)),
None => PlainText("hello!".to_string()),
}
}
}
#[derive(poem_openapi::Object)]
struct Req {
a: String,
b: Option<String>,
}
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
if std::env::var_os("RUST_LOG").is_none() {
std::env::set_var("RUST_LOG", "poem=debug");
}
tracing_subscriber::fmt::init();
let api_service =
OpenApiService::new(Api, "Hello World", "1.0").server("http://localhost:3000/api");
// let ui = api_service.swagger_ui();
Server::new(TcpListener::bind("127.0.0.1:3000"))
.run(Route::new().nest("/api", api_service))
.await
}
Hi Sunli,
When I test dbsession
, it throws some explicit errors about the database which is un-intended.
How can we capture all errors, from all endpoints and from all middleware? so that I can mask it with something more friendly to users.
Thanks!
Looks like this, but the compilation fails.
route()
.at("/hello/:name", get(hello))
.with(AddData::new(db))
.with(RateLimitLayer::new(5, Duration::from_secs(30)).compat())
.with(TimeoutLayer::new(Duration::from_secs(30)).compat())
Hi @sunli829
This is probably due to my limited knowledge of poem, but basically I would like to use async_graphql
with poem with SizeLimit
middleware.
GET app/query => playground
POST app/query => async_graphql with size limit
My code is something like this at the moment
fn app() -> Route {
let schema = ...;
Route::new().at(
"query",
get(graphql_playground)
.post(GraphQL::new(schema))
// .with(SizeLimit::new(CONTENT_LENGTH_LIMIT)),
)
}
let app = Route::new().nest("app", app());
If I uncomment SizeLimit
then I get 400 for GET app/query
as the middleware blocked it.
Can you suggest a way to make this work?
Originally posted by pymongo September 30, 2021
I want to migrate my poem route handler to poem-openapi
I read the document here, but when I add #[oai(extract = true)]
got error:
poem/poem-openapi/src/docs/openapi.md
Line 18 in d53c91c
Here is the StateExtractor define, it works in normal poem handler function
pub struct StateExtractor {
pub cookie_jar: CookieJar,
pub graph_service: Arc<GraphService>,
pub session_store: MemoryStore,
}
#[tonic::async_trait]
impl<'a> FromRequest<'a> for &'a StateExtractor {
type Error = std::convert::Infallible;
async fn from_request(req: &'a Request, _body: &mut RequestBody) -> Result<Self, Self::Error> {
Ok(req.extensions().get::<StateExtractor>().unwrap())
}
}
```</div>
Is there any session example like https://github.com/actix/examples/tree/master/session ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.