actix_web路由报错:`Vec<TextAnnotation>`未实现`Responder` trait
Actix-web返回
Vec<TextAnnotation>提示Responder未实现的问题及解决 问题场景
使用actix_web创建路由时,返回Vec<TextAnnotation>遇到如下编译错误:
the trait `Responder` is not implemented for `Vec<TextAnnotation>`
报错路由代码
use std::str::FromStr; use actix_web::{http::StatusCode, web::Json, Result}; use mongodb::{ bson::{doc, oid::ObjectId}, options::{FindOneAndUpdateOptions, FindOptions, ReturnDocument}, }; use crate::{ database::mongodb::DB, helpers::colors_helpers::{get_next_valid_color, is_color_used, is_valid_color}, middleware::auth_middleware::UserAuthContext, models::text_annotation_model::{ CreateLabelBody, CreateTextAnnotationBody, CreateTokenBody, Label, TextAnnotation, UpdateLabelBody, }, object::{common::Message, error::ApiError}, }; // ... #[get("/")] async fn get_annotations( page: web::Query<i64>, count: web::Query<i64>, req: HttpRequest, ) -> Result<Vec<TextAnnotation>, ApiError> { let auth = get_auth_ctx(&req); let res = AnnotationController::get_page(page.0, count.0, auth); if res.is_err() { return Err(res.err().unwrap()); } Ok(res.unwrap()) }
已实现的Responder代码
use actix_web::{ get, post, web::{self}, HttpRequest, Result, Scope, }; use crate::{ controllers::annotation_controller::AnnotationController, helpers::request_helpers::get_auth_ctx, models::text_annotation_model::{CreateTextAnnotationBody, TextAnnotation}, object::error::ApiError, }; // ... #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TextAnnotation { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option<ObjectId>, pub content: String, pub user_id: ObjectId, pub tokens: Vec<Token>, pub labels: Vec<Label>, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Token { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option<ObjectId>, pub start: i64, pub end: i64, /// reference a label in the labels array pub label: ObjectId, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Label { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option<ObjectId>, pub name: String, pub color: String, } impl Responder for TextAnnotation { type Body = BoxBody; fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse<Self::Body> { HttpResponse::Ok().json(self) } } impl Responder for Label { type Body = BoxBody; fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse<Self::Body> { HttpResponse::Ok().json(self) } } impl Responder for Token { type Body = BoxBody; fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse<Self::Body> { HttpResponse::Ok().json(self) } }
问题原因
你只为单个TextAnnotation、Label、Token类型实现了Responder trait,但actix_web不会自动为集合类型(比如Vec<T>)推导实现Responder——即使T已经实现了该trait,集合类型仍需单独处理。
解决方法
方案1:为Vec<TextAnnotation>实现Responder
直接为集合类型实现trait,逻辑与单个元素一致,返回JSON数组:
use actix_web::{Responder, HttpResponse, BoxBody}; impl Responder for Vec<TextAnnotation> { type Body = BoxBody; fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse<Self::Body> { HttpResponse::Ok().json(self) } }
方案2:用Json包装返回值
无需额外实现trait,直接用web::Json包裹Vec<TextAnnotation>——因为Json<T>已默认实现Responder(只要T实现了Serialize,你的TextAnnotation已经通过#[derive(Serialize)]满足条件):
#[get("/")] async fn get_annotations( page: web::Query<i64>, count: web::Query<i64>, req: HttpRequest, ) -> Result<Json<Vec<TextAnnotation>>, ApiError> { let auth = get_auth_ctx(&req); let res = AnnotationController::get_page(page.0, count.0, auth); if res.is_err() { return Err(res.err().unwrap()); } Ok(Json(res.unwrap())) }
方案3:直接返回HttpResponse
在路由函数中手动构建响应,适合需要自定义状态码、响应头的场景:
#[get("/")] async fn get_annotations( page: web::Query<i64>, count: web::Query<i64>, req: HttpRequest, ) -> Result<HttpResponse, ApiError> { let auth = get_auth_ctx(&req); let res = AnnotationController::get_page(page.0, count.0, auth); if res.is_err() { return Err(res.err().unwrap()); } Ok(HttpResponse::Ok().json(res.unwrap())) }
内容的提问来源于stack exchange,提问作者Riadh Adrani
相关产品推荐
相关产品推荐

