Rust 自建HTTP Server支持图片响应

本博客是在杨旭老师的 rust web 全栈教程项目基础上进行修改,支持了图片资源返回,杨旭老师的rust web链接如下:

https://www.bilibili.com/video/BV1RP4y1G7KFp=1&vd_source=8595fbbf160cc11a0cc07cadacf22951

本人默认读者已经学习了相关基础教程和杨旭老师的相关课程,直接开始针对项目里面对应文件进行修改说明。

一、新增加载图片资源方法

在原先项目加载文件方法同一层,新增支持加载图片资源如下。

pub trait Handler{
    fn handle(res:&HttpRequest) ->HttpResponse;
    fn load_file(file_name:&str) ->Option<Vec<u8>>{
        println!("load_file file_name={}",file_name);
        let default_path = format!("{}/public",env!("CARGO_MANIFEST_DIR"));
        println!("load_file default_path={}",default_path);
        let public_path = env::var("PUBLIC_PATH").unwrap_or(default_path);
        let full_path = format!("{}/{}",public_path,file_name);
        println!("load_file full_path={}",full_path);
        let contents = fs::read(full_path);
        contents.ok()
    }

    fn load_image(image_name:&str) ->Option<Vec<u8>>{
        let default_path = format!("{}/public",env!("CARGO_MANIFEST_DIR"));
        let public_path = env::var("PUBLIC_PATH").unwrap_or(default_path);
        let full_path = format!("{}/{}/{}",public_path,"image",image_name);
        println!("load_image full_path={}",full_path);
        let contents: Result<Vec<u8>, std::io::Error> = fs::read(full_path);
        contents.ok()
    }
}

load_image 方法就是本博客新增方法,根据入参图片名称,去指定public目录下image文件夹下读取图片文件,返回Vec<u8> 字节流。

我们在旧的新增了图片处理分支如下

impl Handler for StaticPageHandler{
    fn handle(req:&HttpRequest) ->HttpResponse{
        let http::httprequest::Resource::Path(s) =&req.resourece;
        let route:Vec<&str> = s.split("/").collect();
        println!("route={}",route[1]);
        if route[1]=="image" {
           match Self::load_image(route[2]){
               Some(content) => {
                let mut map:HashMap<&str,&str> = HashMap::new();
                map.insert("Content-Type","image/webp");
                println!("读取到文件长度{}",content.len());
                return HttpResponse::new("200",Some(map),Some(content));
               },
               None => {
                return HttpResponse::new("404",None,Self::load_file("404.html"))
               }
           }
        }match route[1] {
            "" =>HttpResponse::new("200",None,Self::load_file("index.html")),
            "health" =>HttpResponse::new("200",None,Self::load_file("health.html")),
            path => match Self::load_file(path) {
                Some(contents) =>{
                    let mut map:HashMap<&str,&str> = HashMap::new();
                    if path.ends_with(".css") {
                        map.insert("Content-Type","text/css");
                    }else if path.ends_with(".js") {
                        map.insert("Content-Type","text/javascript");
                    }else {
                        map.insert("Content-Type","text/html");
                    }
                    HttpResponse::new("200",Some(map),Some(contents))
                },
                None => HttpResponse::new("404",None,Self::load_file("404.html"))
            }
        }
    }
}

特别说明点根据加载图片类型不同,map.insert("Content-Type","image/webp");

这里的Content-Type也要对应修改,我这里返回的文件是webp格式。

二、修改HttpResponse 结构体body数据类型

旧项目中由于返回的都是文本内容,body数据类型定义成字符串切片,由于这次改动读取图片是字节流,所以这里的body也需要变更成Option<Vec<u8>>类型

#[derive(Debug,PartialEq,Clone)]
pub struct HttpResponse<'a>{
    version:&'a str,
    status_code:&'a str,
    status_text:&'a str,
    header:Option<HashMap<&'a str,&'a str>>,
    body:Option<Vec<u8>>,
}

三、修改HttpResponse 响应方法

上面已经提到了,旧项目返回的是文本内容,故在发送HttpResponse数据是,直接将数据转化成字符串发送。这里我们为了兼容图片资源的响应,所以body为Vec<u8>类型时就不适用先前的方法。我们对除了body字段,其他字段依然按照旧的逻辑转化成字符串。

impl <'a> From<HttpResponse<'a>> for String{
  fn from(res:HttpResponse) -> String{
    let res1 = res.clone();
    format!("{} {} {}\r\n{}Content-Length:{}\r\n\r\n",
      &res1.version(),
      &res1.status_code(),
      &res1.status_text(),
      &res1.header(),
      &res.body.unwrap().len(),
    )
  }
}

 相比较旧的方法,我们去掉了body参数。

四、返回body数据

上面讲过了,我们在响应转String时,将body数据拆分出来了,那么通过什么方法返回body数据。

    pub fn send_response(&self, write_stream:&mut impl Write) -> Result<(),std::io::Error>{
        let res = self.clone();
        let response_string: String = String::from(res); // from trait
        write_stream.write(response_string.as_bytes()).unwrap();
        write_stream.write(&self.body()).unwrap();
        write_stream.flush().unwrap();
    
        Ok(())
    }

 我们就是通过write_stream.write(&self.body()).unwrap();将body数据再独立发送一下。

整理上来讲改动很小,就是将body数据类型从&str,改成了Vec<u8>,在发送响应时将body拆分出来发送。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Rust 中使用 `HttpResponse` 返回错误响应可以通过 `HttpResponse::build()` 方法进行构建,并在构建过程中设置相应的错误状态码和内容。 以下是一个示例,演示如何使用 `HttpResponse` 返回错误响应: ```rust use actix_web::{HttpResponse, http::StatusCode}; fn handle_request() -> HttpResponse { // 构建错误响应 HttpResponse::build(StatusCode::BAD_REQUEST) .body("Invalid request") } ``` 在上面的示例中,我们使用 `HttpResponse::build()` 方法构建一个 `HttpResponse` 对象,并传递了错误状态码 `StatusCode::BAD_REQUEST`。然后,使用 `body()` 方法设置响应体内容为 "Invalid request"。 你可以根据具体的需求选择适合的状态码,例如 `StatusCode::NOT_FOUND` 表示资源未找到,`StatusCode::INTERNAL_SERVER_ERROR` 表示服务器内部错误等等。 如果你使用的是 Actix-web 框架,可以在路由处理函数中返回 `HttpResponse` 对象来表示错误响应。例如: ```rust use actix_web::{web, App, HttpResponse, HttpServer, Result}; fn handle_request() -> Result<HttpResponse> { // 处理请求并检查是否有错误 if some_error_condition { // 返回错误响应 return Ok(HttpResponse::build(StatusCode::BAD_REQUEST) .body("Invalid request")); } // 处理正常情况 Ok(HttpResponse::Ok().body("Success")) } fn main() { HttpServer::new(|| { App::new() .route("/", web::get().to(handle_request)) }) .bind("127.0.0.1:8080") .unwrap() .run() .unwrap(); } ``` 在上面的示例中,我们在路由处理函数 `handle_request` 中根据错误条件返回了一个错误响应,或者在正常情况下返回一个成功响应。 希望这可以帮助到你!如果你有更多关于错误处理或 `HttpResponse` 的问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值