Skip to main content

WebFingerResponse

Struct WebFingerResponse 

Source
pub struct WebFingerResponse {
    pub subject: JrdUri,
    pub aliases: Option<Vec<JrdUri>>,
    pub properties: Option<BTreeMap<JrdUri, Option<String>>>,
    pub links: Vec<Link>,
}
Expand description

A WebFinger response.

This is the JSON Resource Descriptor (JRD) returned by a WebFinger server. The Rust fields map directly to the top-level members from RFC 7033:

  • subject is required and uses JrdUri because the RFC defines it as the URI of the resource described by the JRD.
  • aliases is an optional list of URI strings, also represented as JrdUri.
  • properties is an optional object with URI property identifiers and string-or-null values.
  • links is the JRD link array. Missing links deserializes as an empty vector.

The response serializes to the RFC JSON shape. It uses typed wrappers for URI-valued and relation-valued fields while keeping builder methods string-friendly for application code.

See RFC 7033 section 4.4.

§Examples

Constructing a response with builders keeps common server code concise:

use webfinger_rs::{Link, WebFingerResponse};

let avatar = Link::builder("http://webfinger.net/rel/avatar")
    .href("https://example.com/avatar.png")
    .build();
let profile = Link::builder("http://webfinger.net/rel/profile-page")
    .href("https://example.com/profile/carol")
    .build();
let response = WebFingerResponse::builder("acct:carol@example.com")
    .alias("https://example.com/profile/carol")
    .property("https://example.com/ns/role", "developer")
    .link(avatar)
    .link(profile)
    .build();

JSON null property values are represented with null_property on the builder:

use webfinger_rs::{Link, WebFingerResponse};

let response = WebFingerResponse::builder("acct:carol@example.com")
    .property("https://example.com/ns/role", "developer")
    .null_property("https://example.com/ns/previous-role")
    .link(
        Link::builder("author")
            .href("https://example.com/people/carol")
            .null_property("https://example.com/ns/legacy-page"),
    )
    .build();

let json = serde_json::to_value(response)?;
assert_eq!(
    json["properties"]["https://example.com/ns/previous-role"],
    serde_json::Value::Null,
);

Response can be used as a response in Axum handlers as it implements axum::response::IntoResponse.

use axum::response::IntoResponse;
use webfinger_rs::{Link, WebFingerRequest, WebFingerResponse};

async fn handler(request: WebFingerRequest) -> WebFingerResponse {
    // ... handle the request ...
    WebFingerResponse::builder("acct:carol@example.com")
        .alias("https://example.com/profile/carol")
        .property("https://example.com/ns/role", "developer")
        .link(
            Link::builder("http://webfinger.net/rel/avatar")
                .href("https://example.com/avatar.png"),
        )
        .build()
}

Fields§

§subject: JrdUri

The subject of the response.

This is the URI of the resource that the JRD describes. RFC 7033 makes it required when a response is returned, so the Rust field is not optional.

JrdUri is used instead of String so relative references are rejected during deserialization and builder construction.

See RFC 7033 section 4.4.1.

§aliases: Option<Vec<JrdUri>>

The aliases of the response.

Aliases are additional URI strings for the same subject. The field is optional because the JSON member may be absent. Each value is a JrdUri for the same reason as Response::subject.

See RFC 7033 section 4.4.2.

§properties: Option<BTreeMap<JrdUri, Option<String>>>

The properties of the response.

JRD properties are a JSON object whose names are URI strings. Values may be strings or JSON null, so the Rust value type is Option<String>. None serializes as a property value of null; it does not omit the property from the map.

A BTreeMap is used for deterministic ordering and to support the standard ordering and hashing traits on Response.

See RFC 7033 section 4.4.3.

§links: Vec<Link>

The links of the response.

This is the JRD links array. A missing JSON member deserializes to an empty vector so code can iterate links without handling a separate absent state.

See RFC 7033 section 4.4.4 and Link.

Implementations§

Source§

impl WebFingerResponse

Source

pub async fn try_from_reqwest( response: Response, ) -> Result<WebFingerResponse, Error>

Available on crate feature reqwest only.

Converts a completed reqwest::Response into a WebFingerResponse.

This is useful when you execute the HTTP request yourself, but still want this crate’s WebFinger response parsing behavior.

The conversion:

  1. Rejects non-success HTTP statuses with reqwest::Response::error_for_status.
  2. Deserializes the response body as JSON into WebFingerResponse.

Both status failures and JSON decoding failures surface as crate::Error::Reqwest.

§Examples
use reqwest::Client;
use webfinger_rs::{WebFingerRequest, WebFingerResponse};

let client = Client::new();
let request = WebFingerRequest::builder("acct:carol@example.com")?
    .host("example.com")
    .build()
    .try_into_reqwest()?;

let response = client.execute(request).await?;
let webfinger = WebFingerResponse::try_from_reqwest(response).await?;
println!("{webfinger:#?}");
Examples found in repository?
examples/client.rs (line 26)
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
11
12    let request = WebFingerRequest::builder(SUBJECT)?
13        .host(HOST)
14        .rel(PROFILE_PAGE_REL)
15        .rel(AVATAR_REL)
16        .build();
17
18    let reqwest_request = request.try_into_reqwest()?;
19    eprintln!("GET {}", reqwest_request.url());
20
21    let client = reqwest::Client::builder()
22        .danger_accept_invalid_certs(true)
23        .https_only(true)
24        .build()?;
25    let response = client.execute(reqwest_request).await?;
26    let response = WebFingerResponse::try_from_reqwest(response).await?;
27
28    println!("Subject: {}", response.subject);
29    for rel in [PROFILE_PAGE_REL, AVATAR_REL] {
30        if let Some(href) = response
31            .links
32            .iter()
33            .find(|link| link.rel.as_ref() == rel)
34            .and_then(|link| link.href.as_ref().map(|href| href.as_ref()))
35        {
36            println!("{rel}: {href}");
37        }
38    }
39    println!("{}", serde_json::to_string_pretty(&response)?);
40    Ok(())
41}
Source§

impl Response

Source

pub fn new<S: AsRef<str>>(subject: S) -> Self

Creates a response with the given subject and no optional JRD members.

This constructor is intended for application-controlled subject strings. It validates the subject as a JrdUri and panics if the string is not an absolute URI. Use Response::try_builder when the subject comes from external input.

Source

pub fn builder<S: AsRef<str>>(subject: S) -> Builder

Creates a Builder with the given subject.

The builder accepts strings at the API boundary and stores typed JRD values internally. This keeps straightforward server responses concise while still producing the RFC-shaped JSON object.

§Examples
use webfinger_rs::{Link, WebFingerResponse};

let avatar =
    Link::builder("http://webfinger.net/rel/avatar").href("https://example.com/avatar.png");
let response = WebFingerResponse::builder("acct:carol@example.com")
    .alias("https://example.com/profile/carol")
    .property("https://example.com/ns/role", "developer")
    .link(avatar)
    .build();
Examples found in repository?
examples/actix.rs (line 97)
68async fn webfinger(request: WebFingerRequest) -> actix_web::Result<WebFingerResponse> {
69    info!("fetching webfinger resource: {:?}", request);
70    let subject = request.resource.to_string();
71    if subject != SUBJECT {
72        let message = format!("{subject} does not exist");
73        return Err(actix_web::error::ErrorNotFound(message))?;
74    }
75    let mut links = Vec::new();
76
77    let profile_rel = Rel::new(PROFILE_PAGE_REL);
78    if request.rels.is_empty() || request.rels.contains(&profile_rel) {
79        links.push(
80            Link::builder(profile_rel)
81                .href(PROFILE_HREF)
82                .title("en", "Carol's profile")
83                .build(),
84        );
85    }
86
87    let avatar_rel = Rel::new(AVATAR_REL);
88    if request.rels.is_empty() || request.rels.contains(&avatar_rel) {
89        links.push(
90            Link::builder(avatar_rel)
91                .href(AVATAR_HREF)
92                .r#type("image/png")
93                .build(),
94        );
95    }
96
97    let response = WebFingerResponse::builder(subject)
98        .alias(PROFILE_HREF)
99        .property(ROLE_PROPERTY, "maintainer")
100        .links(links)
101        .build();
102    Ok(response)
103}
More examples
Hide additional examples
examples/axum.rs (line 100)
71async fn webfinger(request: WebFingerRequest) -> axum::response::Result<WebFingerResponse> {
72    info!("fetching webfinger resource: {:?}", request);
73    let subject = request.resource.to_string();
74    if subject != SUBJECT {
75        let message = format!("{subject} does not exist");
76        return Err((StatusCode::NOT_FOUND, message).into());
77    }
78    let mut links = Vec::new();
79
80    let profile_rel = Rel::new(PROFILE_PAGE_REL);
81    if request.rels.is_empty() || request.rels.contains(&profile_rel) {
82        links.push(
83            Link::builder(profile_rel)
84                .href(PROFILE_HREF)
85                .title("en", "Carol's profile")
86                .build(),
87        );
88    }
89
90    let avatar_rel = Rel::new(AVATAR_REL);
91    if request.rels.is_empty() || request.rels.contains(&avatar_rel) {
92        links.push(
93            Link::builder(avatar_rel)
94                .href(AVATAR_HREF)
95                .r#type("image/png")
96                .build(),
97        );
98    }
99
100    let response = WebFingerResponse::builder(subject)
101        .alias(PROFILE_HREF)
102        .property(ROLE_PROPERTY, "maintainer")
103        .links(links)
104        .build();
105    Ok(response)
106}
Source

pub fn try_builder<S: AsRef<str>>(subject: S) -> Result<Builder, Error>

Tries to create a new Builder with the given subject.

Use this when the subject string has not already been validated by application logic. The returned builder has the same methods as Response::builder.

§Examples
use webfinger_rs::WebFingerResponse;

assert!(WebFingerResponse::try_builder("acct:carol@example.com").is_ok());
assert!(WebFingerResponse::try_builder("/users/carol").is_err());

Trait Implementations§

Source§

impl Clone for Response

Source§

fn clone(&self) -> Response

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Response

Custom debug implementation to avoid printing None fields

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Response

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for Response

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Response

Source§

impl Hash for Response

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl IntoResponse for WebFingerResponse

Available on crate feature axum only.
Source§

fn into_response(self) -> AxumResponse

Converts a WebFingerResponse into an Axum response.

This serializes the body as JSON, sets the Content-Type header to application/jrd+json, and allows cross-origin browser requests with Access-Control-Allow-Origin: * as recommended by RFC 7033 section 5.

Handlers can therefore return WebFingerResponse directly without manually wrapping it in axum::Json or setting the response header themselves.

Mount the route at crate::WELL_KNOWN_PATH so the handler matches the standard WebFinger endpoint path.

See also the crate::axum module docs and the Axum example.

§Example
use axum::{Router, routing::get};
use http::StatusCode;
use webfinger_rs::{Link, Rel, WELL_KNOWN_PATH, WebFingerRequest, WebFingerResponse};

async fn webfinger(request: WebFingerRequest) -> axum::response::Result<WebFingerResponse> {
    let subject = request.resource.to_string();
    if subject != "acct:carol@example.com" {
        return Err((StatusCode::NOT_FOUND, "not found").into());
    }

    let rel = Rel::new("http://webfinger.net/rel/profile-page");
    let response = if request.rels.is_empty() || request.rels.contains(&rel) {
        let link = Link::builder(rel).href("https://example.com/users/carol");
        WebFingerResponse::builder(subject).link(link).build()
    } else {
        WebFingerResponse::builder(subject).build()
    };
    Ok(response)
}

let app = Router::<()>::new().route(WELL_KNOWN_PATH, get(webfinger));
Source§

impl Ord for Response

Source§

fn cmp(&self, other: &Response) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 (const: unstable) · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 (const: unstable) · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 (const: unstable) · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Response

Source§

fn eq(&self, other: &Response) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl PartialOrd for Response

Source§

fn partial_cmp(&self, other: &Response) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 (const: unstable) · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 (const: unstable) · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 (const: unstable) · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 (const: unstable) · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl Responder for WebFingerResponse

Available on crate feature actix only.
Source§

type Body = <Json<Response> as Responder>::Body

Converts a WebFingerResponse into an Actix response.

This serializes the body as JSON and sets the Content-Type header to application/jrd+json, which is the JRD media type used by WebFinger. It also sets Access-Control-Allow-Origin: * as recommended by RFC 7033 section 5.

Handlers can therefore return WebFingerResponse directly without manually wrapping it in actix_web::web::Json or setting the response header themselves.

See also the crate::actix module docs and the Actix example.

§Example
use actix_web::{get, App};
use webfinger_rs::{Link, Rel, WebFingerRequest, WebFingerResponse};

#[get("/.well-known/webfinger")]
async fn webfinger(request: WebFingerRequest) -> actix_web::Result<WebFingerResponse> {
    let subject = request.resource.to_string();
    let rel = Rel::new("http://webfinger.net/rel/profile-page");
    let response = if request.rels.is_empty() || request.rels.contains(&rel) {
        let link = Link::builder(rel).href("https://example.com/users/carol");
        WebFingerResponse::builder(subject).link(link).build()
    } else {
        WebFingerResponse::builder(subject).build()
    };
    Ok(response)
}

let app = App::new().service(webfinger);
Source§

fn respond_to(self, request: &HttpRequest) -> HttpResponse<Self::Body>

Convert self to HttpResponse.
Source§

fn customize(self) -> CustomizeResponder<Self>
where Self: Sized,

Wraps responder to allow alteration of its response. Read more
Source§

impl Serialize for Response

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Response

Source§

impl TryFrom<&Response> for Response<()>

Source§

type Error = Error

The type returned in the event of a conversion error.
Source§

fn try_from(_: &WebFingerResponse) -> Result<Response<()>, Error>

Performs the conversion.
Source§

impl TryFrom<Response> for WebFingerResponse

Available on crate feature reqwest only.
Source§

type Error = Error

The type returned in the event of a conversion error.
Source§

fn try_from<'async_trait>( response: Response, ) -> Pin<Box<dyn Future<Output = Result<WebFingerResponse, Error>> + Send + 'async_trait>>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Comparable<K> for Q
where Q: Ord + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T, S> Handler<IntoResponseHandler, S> for T
where T: IntoResponse + Clone + Send + Sync + 'static,

Source§

type Future = Ready<Response<Body>>

The type of future calling this handler returns.
Source§

fn call( self, _req: Request<Body>, _state: S, ) -> <T as Handler<IntoResponseHandler, S>>::Future

Call the handler with the given request.
Source§

fn layer<L>(self, layer: L) -> Layered<L, Self, T, S>
where L: Layer<HandlerService<Self, T, S>> + Clone, <L as Layer<HandlerService<Self, T, S>>>::Service: Service<Request<Body>>,

Apply a tower::Layer to the handler. Read more
Source§

fn with_state(self, state: S) -> HandlerService<Self, T, S>

Convert the handler into a Service by providing the state
Source§

impl<H, T> HandlerWithoutStateExt<T> for H
where H: Handler<T, ()>,

Source§

fn into_service(self) -> HandlerService<H, T, ()>

Convert the handler into a Service and no state.
Source§

fn into_make_service(self) -> IntoMakeService<HandlerService<H, T, ()>>

Convert the handler into a MakeService and no state. Read more
Source§

fn into_make_service_with_connect_info<C>( self, ) -> IntoMakeServiceWithConnectInfo<HandlerService<H, T, ()>, C>

Available on crate feature tokio only.
Convert the handler into a MakeService which stores information about the incoming connection and has no state. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into<'async_trait>( self, ) -> Pin<Box<dyn Future<Output = Result<U, <U as TryFrom<T>>::Error>> + 'async_trait>>
where T: 'async_trait,

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more