Repository: actix/actix-web
File: src/types/json.rs (Rust)

Code before:
///     username: String,
/// }
///
/// /// deserialize `Info` from request's body, max payload size is 4kb
/// async fn index(info: web::Json<Info>) -> String {
///     format!("Welcome {}!", info.username)
/// }
///
/// /// Return either a 400 or 415, and include the error message from serde
/// /// in the response body
/// fn json_error_handler(err: error::JsonPayloadError, _req: &HttpRequest) -> error::Error {
///     let detail = err.to_string();
///     let response = match &err {
///         error::JsonPayloadError::ContentType => {
///             HttpResponse::UnsupportedMediaType().content_type("text/plain").body(detail)
///         }
///         _ => HttpResponse::BadRequest().content_type("text/plain").body(detail),
///     };
///     error::InternalError::from_response(err, response).into()
/// }
///
/// fn main() {
///     let app = App::new().service(
///         web::resource("/index.html")
///             .app_data(
///                 // Json extractor configuration for only the `Info` struct.
///                 web::Json::<Info>::configure(|cfg| {
///                     cfg.limit(4096)
///                        .content_type(|mime| {  // <- accept text/plain content type
///                            mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
///                        })
///                        .error_handler(json_error_handler)  // Use our custom error response
///             }))
///             .route(web::post().to(index))
///     );
/// }
/// ```
#[derive(Clone)]
pub struct JsonConfig {
    limit: usize,
    err_handler:
        Option<Arc<dyn Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync>>,
    content_type: Option<Arc<dyn Fn(mime::Mime) -> bool + Send + Sync>>,
}

impl JsonConfig {
    /// Change max size of payload. By default max size is 32Kb
    pub fn limit(mut self, limit: usize) -> Self {
        self.limit = limit;
        self
    }

Review comment (refactor):
IMHO it would read better like that:

```suggestion
///                 // JSON extractor configuration for the `Info` struct only.
```

Code after:
impl JsonConfig {
    /// Change max size of payload. By default max size is 32Kb
    pub fn limit(mut self, limit: usize) -> Self {
        self.limit = limit;
        self
    }

    /// Set custom error handler
    pub fn error_handler<F>(mut self, f: F) -> Self
    where
        F: Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync + 'static,
    {
        self.err_handler = Some(Arc::new(f));
        self
    }

    /// Set predicate for allowed content types
    pub fn content_type<F>(mut self, predicate: F) -> Self
    where
        F: Fn(mime::Mime) -> bool + Send + Sync + 'static,
    {
        self.content_type = Some(Arc::new(predicate));
        self
    }

    /// Extract payload config from app data. Check both `T` and `Data<T>`, in that order, and fall
    /// back to the default payload config.
    fn from_req(req: &HttpRequest) -> &Self {
        req.app_data::<Self>()
            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))
            .unwrap_or_else(|| &DEFAULT_CONFIG)
    }
}

// Allow shared refs to default.
const DEFAULT_CONFIG: JsonConfig = JsonConfig {
    limit: 32_768, // 2^15 bytes, (~32kB)
    err_handler: None,
    content_type: None,
};

impl Default for JsonConfig {
    fn default() -> Self {
        DEFAULT_CONFIG.clone()
    }
}

/// Request's payload json parser, it resolves to a deserialized `T` value.
/// This future could be used with `ServiceRequest` and `ServiceFromRequest`.
///
/// Returns error: