0

Background

I am using the actix-web crate to implement a REST API and the paperclip crate to generate a swagger spec endpoint.

The Problem

In the swagger ui, the response type is shown as application/json, but the model of the response is not shown. (When the specification defines the model, the swagger UI shows an Example of the model)

This is the code that I am using:

use super::preset::load_customers;
use super::property::Customer;
use crate::Error;

use actix_web::HttpResponse;
use deadpool_postgres::Pool;
use paperclip::actix::api_v2_operation;
use paperclip::actix::Apiv2Schema;

use paperclip::actix::web;
use serde::Serialize;

pub fn control_routes() -> web::Scope {
    web::scope("/api/v1").service(web::resource("/customers").route(web::get().to(get_customers)))
}

#[derive(Serialize, Apiv2Schema)]
#[serde(tag = "type")]
struct CustomerResponse {
    customer: Vec<Customer>,
}

/// Get all customers
#[api_v2_operation]
pub async fn get_customers(_db_pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
    let customers = load_customers()?;
    Ok(HttpResponse::Ok().body(CustomerResponse {
        customer: customers,
    }))
}

I think that from the code it is clear why the generated specification does not define the model of the response type. The reason is that the CustomerResponse object is converted to a json string before placing it in the HttpResponse, which, I assume, loses all the type information.

What I Have Tried

I replaced the call to json on the HttpResponse to body like so:

    Ok(HttpResponse::Ok().body(CustomerResponse {
        customer: customers,
    }))

However this fails to compile with the following error:

the trait bound `actix_web::dev::Body: std::convert::From<rest::control::handlers::CustomerResponse>` is not satisfied

the trait `std::convert::From<rest::control::handlers::CustomerResponse>` is not implemented for `actix_web::dev::Body`

help: the following implementations were found:
        <actix_web::dev::Body as std::convert::From<&'a std::string::String>>
        <actix_web::dev::Body as std::convert::From<&'static [u8]>>
        <actix_web::dev::Body as std::convert::From<&'static str>>
        <actix_web::dev::Body as std::convert::From<actix_web::body::BodyStream<S, E>>>
      and 6 others
note: required because of the requirements on the impl of `std::convert::Into<actix_web::dev::Body>` for `rest::control::handlers::CustomerResponse`rustc(E0277)
handlers.rs(27, 27): the trait `std::convert::From<rest::control::handlers::CustomerResponse>` is not implemented for `actix_web::dev::Body`
the trait bound `actix_web::dev::Body: From<CustomerResponse>` is not satisfied
the following implementations were found:
  <actix_web::dev::Body as From<&'a std::string::String>>
  <actix_web::dev::Body as From<&'static [u8]>>
  <actix_web::dev::Body as From<&'static str>>
  <actix_web::dev::Body as From<BodyStream<S, E>>>
and 6 others
required because of the requirements on the impl of `Into<actix_web::dev::Body>` for `CustomerResponse`rustcE0277
actix_http::response::ResponseBuilder
pub fn body<B>(&mut self, body: B) -> Response
where
    B: Into<Body>,
Set a body and generate Response.

ResponseBuilder can not be used after this call.

Summary

What changes should I make to my code in order that paperclip should generate a specification for the response type?

E_net4
  • 27,810
  • 13
  • 101
  • 139
David Sackstein
  • 500
  • 1
  • 5
  • 19

1 Answers1

1

From the docs, you will need to use the paperclip responder:

use paperclip::actix::{
    web::{self, Json}, // <- Include the Responder
};

/// .....
/// Then use it in your method 
/// Get all customers
#[api_v2_operation]
pub async fn get_customers(_db_pool: web::Data<Pool>) -> Result<Json<CustomerResponse>, Error> {
    let customers = load_customers()?;
    Ok(CustomerResponse {
        customer: customers,
    })
}

You can read more about what is happening behind the scene on Actix docs

Njuguna Mureithi
  • 3,506
  • 1
  • 21
  • 41