Skip to content

Commit 86d4765

Browse files
committed
Support POST binding
1 parent c9828b5 commit 86d4765

File tree

2 files changed

+210
-8
lines changed

2 files changed

+210
-8
lines changed

openapi/openapi.yaml

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ paths:
2222
other resource identified by the DID URL.</p>
2323
<p>See the <a href="<a href="https://www.w3.org/TR/did-resolution/">DID
2424
Resolution</a> specification for additional details.</p>
25-
operationId: resolve
25+
operationId: resolveGet
2626
tags:
2727
- Universal Resolver
2828
parameters:
@@ -62,12 +62,109 @@ paths:
6262
- in: query
6363
name: options
6464
schema:
65-
oneOf:
66-
- $ref: "#/components/schemas/ResolutionOptions"
67-
- $ref: "#/components/schemas/DereferencingOptions"
68-
description: The options for resolving the DID or dereferencing the DID URL.
65+
$ref: "#/components/schemas/ResolveGetQuery"
6966
style: form
7067
explode: true
68+
description: The options for resolving the DID or dereferencing the DID URL.
69+
responses:
70+
"200":
71+
description: successfully resolved!
72+
content:
73+
application/did:
74+
schema:
75+
type: object
76+
description: The DID document.
77+
example:
78+
"@context": https://www.w3.org/ns/did/v1.1
79+
id: did:indy:sovrin:WRfXPg8dantKVubE3HX8pw
80+
verificationMethod:
81+
- id: did:indy:sovrin:WRfXPg8dantKVubE3HX8pw#key-1
82+
type: Ed25519VerificationKey2018
83+
publicKeyBase58: H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV
84+
application/did-resolution:
85+
schema:
86+
$ref: "#/components/schemas/ResolutionResult"
87+
application/did-url-dereferencing:
88+
schema:
89+
$ref: "#/components/schemas/DereferencingResult"
90+
"400":
91+
description: Invalid DID or DID URL.
92+
"404":
93+
description: DID or DID URL not found.
94+
"406":
95+
description: Representation not supported.
96+
"410":
97+
description: Successfully resolved, but DID is deactivated.
98+
content:
99+
application/did:
100+
schema:
101+
type: object
102+
description: The deactivated DID document.
103+
application/did-resolution:
104+
schema:
105+
$ref: "#/components/schemas/ResolutionResult"
106+
application/did-url-dereferencing:
107+
schema:
108+
$ref: "#/components/schemas/DereferencingResult"
109+
"500":
110+
description: Internal Error.
111+
"501":
112+
description: DID method not supported.
113+
post:
114+
summary: Resolve a DID / Dereference a DID URL
115+
description: <p>This endpoint either resolves a DID, or dereferences a DID URL.
116+
When resolving a DID,
117+
it takes the DID and resolution options as inputs, and
118+
the output is a DID document plus metadata.
119+
When dereferencing a DID URL,
120+
it takes the DID URL and dereferencing options as inputs, and
121+
the output is a DID document, a part of a DID document, or some
122+
other resource identified by the DID URL.</p>
123+
<p>See the <a href="<a href="https://www.w3.org/TR/did-resolution/">DID
124+
Resolution</a> specification for additional details.</p>
125+
operationId: resolvePost
126+
tags:
127+
- Universal Resolver
128+
parameters:
129+
- in: path
130+
required: true
131+
name: identifier
132+
schema:
133+
type: string
134+
description: The DID to be resolved, or the DID URL to be dereferenced.
135+
examples:
136+
example1:
137+
value: did:indy:sovrin:builder:VbPQNHsvoLZdaNU7fTBeFx
138+
description: A DID using the `indy` method.
139+
example2:
140+
value: did:ion:EiClkZMDxPKqC9c-umQfTkR8vvZ9JPhl_xLDI9Nfk38w5w
141+
description: A DID using the `ion` method.
142+
example3:
143+
value: did:ebsi:z25ZZFS7FweHsm9MX2Qvc6gc
144+
description: A DID using the `ebsi` method.
145+
- in: header
146+
required: false
147+
name: Accept
148+
schema:
149+
type: string
150+
description: The requested media type of the DID document representation or
151+
DID resolution result.
152+
examples:
153+
application/did:
154+
value: application/did
155+
description: Media type of a DID document.
156+
application/did-resolution:
157+
value: application/did-resolution
158+
description: Media type of a DID resolution result.
159+
application/did-url-dereferencing:
160+
value: application/did-url-dereferencing
161+
description: Media type of a DID URL dereferencing result.
162+
requestBody:
163+
description: The options for resolving the DID or dereferencing the DID URL.
164+
content:
165+
application/json:
166+
schema:
167+
$ref: "#/components/schemas/ResolvePostBody"
71168
responses:
72169
"200":
73170
description: successfully resolved!
@@ -202,6 +299,14 @@ paths:
202299
humanReadable: false
203300
components:
204301
schemas:
302+
ResolveGetQuery:
303+
oneOf:
304+
- $ref: "#/components/schemas/ResolutionOptions"
305+
- $ref: "#/components/schemas/DereferencingOptions"
306+
ResolvePostBody:
307+
oneOf:
308+
- $ref: "#/components/schemas/ResolutionOptions"
309+
- $ref: "#/components/schemas/DereferencingOptions"
205310
ResolutionOptions:
206311
description: The DID resolution options. See https://www.w3.org/TR/did-resolution/#did-resolution-options
207312
type: object

uni-resolver-web/src/main/java/uniresolver/web/servlet/ResolveServlet.java

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
6969
} else {
7070
options = objectMapper.readValue(URLDecoder.decode(request.getQueryString(), StandardCharsets.UTF_8), LinkedHashMap.class);
7171
}
72-
} else if (request.getQueryString() != null) {
73-
options.putAll(objectMapper.readValue(request.getQueryString(), Map.class));
72+
if (log.isDebugEnabled()) log.debug("Options from query: " + options);
7473
}
7574
} else {
7675
identifier = path;
@@ -109,6 +108,104 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
109108

110109
if (log.isDebugEnabled()) log.debug("Using options: " + options);
111110

111+
// execute
112+
113+
this.execute(response, identifier, options, httpAcceptMediaTypes, isResolve);
114+
}
115+
116+
@Override
117+
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
118+
119+
// read request
120+
121+
request.setCharacterEncoding("UTF-8");
122+
response.setCharacterEncoding("UTF-8");
123+
124+
String contextPath = request.getContextPath();
125+
String servletPath = request.getServletPath();
126+
String requestPath = request.getRequestURI();
127+
128+
if (log.isDebugEnabled()) log.debug("Incoming request: " + requestPath);
129+
130+
String path = requestPath.substring(contextPath.length() + servletPath.length());
131+
if (path.startsWith("/")) path = path.substring(1);
132+
133+
if (path.isEmpty()) {
134+
ServletUtil.sendResponse(response, HttpServletResponse.SC_BAD_REQUEST, "No identifier found in request.");
135+
return;
136+
}
137+
138+
// parse request
139+
140+
String identifier;
141+
Map<String, Object> options = new LinkedHashMap<>();
142+
boolean isResolve;
143+
144+
if (path.startsWith("did%3A")) {
145+
identifier = URLDecoder.decode(path, StandardCharsets.UTF_8);
146+
if (request.getQueryString() != null) {
147+
if (request.getQueryString().contains("=")) {
148+
for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements(); ) {
149+
String parameterName = e.nextElement();
150+
String parameterValue = request.getParameter(parameterName);
151+
options.put(parameterName, parameterValue);
152+
}
153+
} else {
154+
options = objectMapper.readValue(URLDecoder.decode(request.getQueryString(), StandardCharsets.UTF_8), LinkedHashMap.class);
155+
}
156+
if (log.isDebugEnabled()) log.debug("Options from query: " + options);
157+
}
158+
} else {
159+
identifier = path;
160+
if (request.getQueryString() != null) identifier += "?" + request.getQueryString();
161+
}
162+
isResolve = (! identifier.contains("/")) && (! identifier.contains("?")) && (! identifier.contains("#"));
163+
164+
Map<String, Object> bodyOptions = objectMapper.readValue(request.getReader(), Map.class);
165+
if (log.isDebugEnabled()) log.debug("Options from body: " + bodyOptions);
166+
if (bodyOptions != null) {
167+
options.keySet().forEach(bodyOptions::remove);
168+
options.putAll(bodyOptions);
169+
}
170+
171+
if (log.isInfoEnabled()) log.info("Incoming identifier: " + identifier + " (isResolve=" + isResolve + "), incoming options: " + options);
172+
173+
// prepare options
174+
175+
String httpXConfigHeader = request.getHeader("X-Config");
176+
if (log.isInfoEnabled()) log.info("Incoming X-Config: header string: " + httpXConfigHeader);
177+
Map<String, Object> httpXConfigHeaderMap = httpXConfigHeader == null ? null : (Map<String, Object>) objectMapper.readValue(httpXConfigHeader, Map.class);
178+
if (httpXConfigHeaderMap != null) {
179+
options.keySet().forEach(httpXConfigHeaderMap::remove);
180+
options.putAll(httpXConfigHeaderMap);
181+
}
182+
183+
String httpAcceptHeader = request.getHeader("Accept");
184+
if (log.isInfoEnabled()) log.info("Incoming Accept: header string: " + httpAcceptHeader);
185+
186+
List<MediaType> httpAcceptMediaTypes = httpAcceptHeader == null ? null : MediaType.parseMediaTypes(httpAcceptHeader);
187+
if (httpAcceptMediaTypes != null) MimeTypeUtils.sortBySpecificity(httpAcceptMediaTypes);
188+
if (httpAcceptHeader != null) options.put("_http_accept", httpAcceptMediaTypes);
189+
190+
if (! options.containsKey("accept")) {
191+
if (isResolve) {
192+
if (httpAcceptMediaTypes == null) httpAcceptMediaTypes = Collections.singletonList(MediaType.parseMediaType(ResolveResult.MEDIA_TYPE));
193+
options.put("accept", HttpBindingServerUtil.resolveAcceptForHttpAccepts(httpAcceptMediaTypes));
194+
} else {
195+
if (httpAcceptMediaTypes == null) httpAcceptMediaTypes = Collections.singletonList(MediaType.parseMediaType(DereferenceResult.MEDIA_TYPE));
196+
options.put("accept", HttpBindingServerUtil.dereferenceAcceptForHttpAccepts(httpAcceptMediaTypes));
197+
}
198+
}
199+
200+
if (log.isDebugEnabled()) log.debug("Using options: " + options);
201+
202+
// execute
203+
204+
this.execute(response, identifier, options, httpAcceptMediaTypes, isResolve);
205+
}
206+
207+
private void execute(HttpServletResponse response, String identifier, Map<String, Object> options, List<MediaType> httpAcceptMediaTypes, boolean isResolve) throws IOException {
208+
112209
// execute the request
113210

114211
Result result;
@@ -233,6 +330,6 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
233330
ServletUtil.sendResponse(
234331
response,
235332
HttpServletResponse.SC_NOT_ACCEPTABLE,
236-
"Not acceptable media types " + httpAcceptHeader);
333+
"Not acceptable media types " + httpAcceptMediaTypes);
237334
}
238335
}

0 commit comments

Comments
 (0)