当内容类型不是“ application / json”时,如何在Rocket中解析JSON主体?

问题描述

我正在尝试将浏览器直接通过POST提交的JSON CSP Record解析为嵌套结构:

{"csp-report":{"document-uri":"http://localhost:8000/demo/","referrer":"","violated-directive":"img-src","effective-directive":"img-src","original-policy":"default-src 'self'; report-uri /.well-kNown/csp-violation","disposition":"report","blocked-uri":"https://www.google.com/logos/doodles/2020/googles-22nd-birthday-6753651837108550-law.gif","line-number":47,"source-file":"http://localhost:8000/demo/","status-code":200,"script-sample":""}}

发送以下标头:

Accept: */*
Accept-Encoding: gzip,deflate,br
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 442
Content-Type: application/csp-report
Host: localhost:8000
Origin: http://localhost:8000
Pragma: no-cache
Referer: http://localhost:8000/demo/
Sec-Fetch-Dest: report
sec-fetch-mode: no-cors
sec-fetch-site: same-origin
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/85.0.4183.102 Safari/537.36

我遵循了Rocket JSON data guide,但是请求生成Unprocessable Entity (422),但我不知道为什么。

#[derive(Serialize,Deserialize,Debug)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unkNown_fields)]
struct Report {
    #[serde(with = "serde_with::json::nested")]
    csp_report: ReportBody,}

#[derive(Serialize,Debug)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unkNown_fields)]
struct ReportBody {
    blocked_uri: String,disposition: String,document_uri: String,effective_directive: String,line_number: u128,original_policy: String,referrer: String,script_sample: String,source_file: String,status_code: u16,violated_directive: String,}

#[post(
    "/.well-kNown/csp-violation",format = "application/csp-report",data = "<_report>"
)]
fn record(_report: Json<Report>) -> Status {
    Status::NoContent
}

fn main() {
    rocket::ignite().mount("/",routes![record]).launch();
}

我的猜测是由于标题Content-Type: application/csp-report不能更改,因为浏览器会自动发送报告。

解决方法

来自rocket_contrib的

JSON很方便,但不是必需的。您仍然可以使用serde自行从原始数据中解析JSON(以下示例是通过异步Rocket完成的)。这可能会绕过标题的任何问题:

use rocket::Data;
...

#[post(
    "/.well-known/csp-violation",data = "<data>"
)]
async fn record(data: Data) -> Status {
    let body = match data.open(128.kilobytes()).stream_to_string().await {
        Ok(d) => d,Err(_) => return Status::NoContent,// or whatever error
    };
    let report: Report = match serde_json::from_str(&body) {
        Ok(a) => a,// or whatever error
    };
    // do what you want with report here
    ...
    
    Status::NoContent
}