|
| 1 | +# Background |
| 2 | +The WebView2 team has been asked for an API to get the response for a web |
| 3 | +resource as it was received and to provide request headers not available when |
| 4 | +`WebResourceRequested` event is raised (such as HTTP Authentication headers). |
| 5 | +The `WebResourceResponseReceived` event provides such response representation |
| 6 | +and exposes the request as committed. A web request is any URI resolution the |
| 7 | +WebView performs. This includes declarative `<img src="...">` from HTML, |
| 8 | +implicit `favicon.ico` lookups, and JavaScript in the document calling the |
| 9 | +`fetch(...)` API. |
| 10 | + |
| 11 | +In this document we describe the new API. We'd appreciate your feedback. |
| 12 | + |
| 13 | +# Description |
| 14 | +The `WebResourceResponseReceived` event allows developers to inspect the |
| 15 | +response object from URL requests (such as HTTP/HTTPS, file and data). A key |
| 16 | +scenario is to allow developers to get Auth headers from an HTTP response to |
| 17 | +authenticate other tools they're using, since the Auth headers are not exposed |
| 18 | +in the `WebResourceRequested` event. |
| 19 | + |
| 20 | +This event is raised when the WebView receives the response for a request for a |
| 21 | +web resource. It provides access to both the response as it was received and the |
| 22 | +request as it was committed, including modifications made by the network stack |
| 23 | +(such as the adding of HTTP Authorization headers). The app can use this event |
| 24 | +to view the actual request and response for a web resource. Modifications to the |
| 25 | +request object are set but have no effect on WebView processing it. There is no |
| 26 | +ordering guarantee between WebView processing the response and the host app's |
| 27 | +event handler running. |
| 28 | + |
| 29 | +When the event is raised, the WebView will pass a |
| 30 | +`WebResourceResponseReceivedEventArgs`, which lets the app view the request and |
| 31 | +response. To get the response content, call `GetContent`/`GetContentAsync` on |
| 32 | +the `CoreWebView2WebResourceResponseView` object from the event args. |
| 33 | + |
| 34 | +# Examples |
| 35 | +The following code snippets demonstrate how the `WebResourceResponseReceived` |
| 36 | +event can be used: |
| 37 | + |
| 38 | +## COM |
| 39 | +```cpp |
| 40 | +EventRegistrationToken m_webResourceResponseReceivedToken = {}; |
| 41 | + |
| 42 | +m_webview->add_WebResourceResponseReceived( |
| 43 | + Callback<ICoreWebView2WebResourceResponseReceivedEventHandler>( |
| 44 | + [this](ICoreWebView2* webview, ICoreWebView2WebResourceResponseReceivedEventArgs* args) |
| 45 | + -> HRESULT { |
| 46 | + // The request object as committed |
| 47 | + wil::com_ptr<ICoreWebView2WebResourceRequest> webResourceRequest; |
| 48 | + CHECK_FAILURE(args->get_Request(&webResourceRequest)); |
| 49 | + // The response object as received |
| 50 | + wil::com_ptr<ICoreWebView2WebResourceResponseView> webResourceResponse; |
| 51 | + CHECK_FAILURE(args->get_Response(&webResourceResponse)); |
| 52 | + |
| 53 | + // Get body content for the response |
| 54 | + webResourceResponse->GetContent( |
| 55 | + Callback< |
| 56 | + ICoreWebView2WebResourceResponseViewGetContentCompletedHandler>( |
| 57 | + [this, webResourceRequest, webResourceResponse](HRESULT result, IStream* content) { |
| 58 | + // The response content might have failed to load. |
| 59 | + bool getContentSucceeded = SUCCEEDED(result); |
| 60 | + |
| 61 | + // The stream will be null if no content was found for the response. |
| 62 | + if (content) { |
| 63 | + DoSomethingWithContent(content); |
| 64 | + } |
| 65 | + |
| 66 | + std::wstring message = |
| 67 | + L"{ \"kind\": \"event\", \"name\": " |
| 68 | + L"\"WebResourceResponseReceived\", \"args\": {" |
| 69 | + L"\"request\": " + |
| 70 | + RequestToJsonString(webResourceRequest.get()) + |
| 71 | + L", " |
| 72 | + L"\"response\": " + |
| 73 | + ResponseToJsonString(webResourceResponse.get(), content) + L"}"; |
| 74 | + |
| 75 | + message += |
| 76 | + WebViewPropertiesToJsonString(m_webview.get()); |
| 77 | + message += L"}"; |
| 78 | + PostEventMessage(message); |
| 79 | + return S_OK; |
| 80 | + }) |
| 81 | + .Get()); |
| 82 | + |
| 83 | + return S_OK; |
| 84 | + }) |
| 85 | + .Get(), |
| 86 | + &m_webResourceResponseReceivedToken); |
| 87 | +``` |
| 88 | +
|
| 89 | +## C# |
| 90 | +```c# |
| 91 | +WebView.WebResourceResponseReceived += WebView_WebResourceResponseReceived; |
| 92 | +
|
| 93 | +// Note: modifications made to request are set but have no effect on WebView processing it. |
| 94 | +private async void WebView_WebResourceResponseReceived(CoreWebView2 sender, CoreWebView2WebResourceResponseReceivedEventArgs e) |
| 95 | +{ |
| 96 | + // Actual headers sent with request |
| 97 | + foreach (var current in e.Request.Headers) |
| 98 | + { |
| 99 | + Console.WriteLine(current); |
| 100 | + } |
| 101 | +
|
| 102 | + // Headers in response received |
| 103 | + foreach (var current in e.Response.Headers) |
| 104 | + { |
| 105 | + Console.WriteLine(current); |
| 106 | + } |
| 107 | +
|
| 108 | + // Status code from response received |
| 109 | + int status = e.Response.StatusCode; |
| 110 | + if (status == 200) |
| 111 | + { |
| 112 | + // Handle |
| 113 | + Console.WriteLine("Request succeeded!"); |
| 114 | +
|
| 115 | + // Get response body |
| 116 | + try |
| 117 | + { |
| 118 | + System.IO.Stream content = await e.Response.GetContentAsync(); |
| 119 | + // Null will be returned if no content was found for the response. |
| 120 | + if (content) |
| 121 | + { |
| 122 | + DoSomethingWithResponseContent(content); |
| 123 | + } |
| 124 | + } |
| 125 | + catch (COMException ex) |
| 126 | + { |
| 127 | + // A COMException will be thrown if the content failed to load. |
| 128 | + } |
| 129 | + } |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | + |
| 134 | +# Remarks |
| 135 | +`ICoreWebView2WebResourceResponseViewGetContentCompletedHandler` will be |
| 136 | +invoked with a failure errorCode if the content failed to load. |
| 137 | +Calling `CoreWebView2WebResourceResponseView.GetContentAsync` will throw a |
| 138 | +`COMException` if the content failed to load. |
| 139 | + |
| 140 | + |
| 141 | +# API Notes |
| 142 | +See [API Details](#api-details) section below for API reference. |
| 143 | + |
| 144 | + |
| 145 | +# API Details |
| 146 | +## COM |
| 147 | +```cpp |
| 148 | +library WebView2 |
| 149 | +{ |
| 150 | +// ... |
| 151 | + |
| 152 | +interface ICoreWebView2 : IUnknown |
| 153 | +{ |
| 154 | + // ... |
| 155 | + |
| 156 | + /// Add an event handler for the WebResourceResponseReceived event. |
| 157 | + /// WebResourceResponseReceived is raised when the WebView receives the |
| 158 | + /// response for a request for a web resource (any URI resolution performed by |
| 159 | + /// the WebView; such as HTTP/HTTPS, file and data requests from redirects, |
| 160 | + /// navigations, declarations in HTML, implicit favicon lookups, and fetch API |
| 161 | + /// usage in the document). The host app can use this event to view the actual |
| 162 | + /// request and response for a web resource. There is no guarantee about the |
| 163 | + /// order in which the WebView processes the response and the host app's |
| 164 | + /// handler runs. The app's handler will not block the WebView from processing |
| 165 | + /// the response. |
| 166 | + HRESULT add_WebResourceResponseReceived( |
| 167 | + [in] ICoreWebView2WebResourceResponseReceivedEventHandler* eventHandler, |
| 168 | + [out] EventRegistrationToken* token); |
| 169 | + /// Remove an event handler previously added with |
| 170 | + /// add_WebResourceResponseReceived. |
| 171 | + HRESULT remove_WebResourceResponseReceived( |
| 172 | + [in] EventRegistrationToken token); |
| 173 | +} |
| 174 | + |
| 175 | +/// The caller implements this interface to receive WebResourceResponseReceived |
| 176 | +/// events. |
| 177 | +interface ICoreWebView2WebResourceResponseReceivedEventHandler : IUnknown |
| 178 | +{ |
| 179 | + /// Called to provide the implementer with the event args for the |
| 180 | + /// corresponding event. |
| 181 | + HRESULT Invoke( |
| 182 | + [in] ICoreWebView2* sender, |
| 183 | + [in] ICoreWebView2WebResourceResponseReceivedEventArgs* args); |
| 184 | +} |
| 185 | + |
| 186 | +/// Event args for the WebResourceResponseReceived event. |
| 187 | +interface ICoreWebView2WebResourceResponseReceivedEventArgs : IUnknown |
| 188 | +{ |
| 189 | + /// The request object for the web resource, as committed. This includes |
| 190 | + /// headers added by the network stack that were not be included during the |
| 191 | + /// associated WebResourceRequested event, such as Authentication headers. |
| 192 | + /// Modifications to this object have no effect on how the request is |
| 193 | + /// processed as it has already been sent. |
| 194 | + [propget] HRESULT Request( |
| 195 | + [out, retval] ICoreWebView2WebResourceRequest** request); |
| 196 | + /// View of the response object received for the web resource. |
| 197 | + [propget] HRESULT Response( |
| 198 | + [out, retval] ICoreWebView2WebResourceResponseView** response); |
| 199 | +} |
| 200 | + |
| 201 | +/// View of the HTTP representation for a web resource response. The properties |
| 202 | +/// of this object are not mutable. This response view is used with the |
| 203 | +/// WebResourceResponseReceived event. |
| 204 | +interface ICoreWebView2WebResourceResponseView : IUnknown |
| 205 | +{ |
| 206 | + /// The HTTP response headers as received. |
| 207 | + [propget] HRESULT Headers( |
| 208 | + [out, retval] ICoreWebView2HttpResponseHeaders** headers); |
| 209 | + /// The HTTP response status code. |
| 210 | + [propget] HRESULT StatusCode([out, retval] int* statusCode); |
| 211 | + /// The HTTP response reason phrase. |
| 212 | + [propget] HRESULT ReasonPhrase([out, retval] LPWSTR* reasonPhrase); |
| 213 | + |
| 214 | + /// Get the response content asynchronously. The handler will receive the |
| 215 | + /// response content stream. |
| 216 | + /// If this method is being called again before a first call has completed, |
| 217 | + /// the handler will be invoked at the same time the handlers from prior calls |
| 218 | + /// are invoked. |
| 219 | + /// If this method is being called after a first call has completed, the |
| 220 | + /// handler will be invoked immediately. |
| 221 | + HRESULT GetContent( |
| 222 | + [in] ICoreWebView2WebResourceResponseViewGetContentCompletedHandler* handler); |
| 223 | +} |
| 224 | + |
| 225 | +/// The caller implements this interface to receive the result of the |
| 226 | +/// ICoreWebView2WebResourceResponseView::GetContent method. |
| 227 | +interface ICoreWebView2WebResourceResponseViewGetContentCompletedHandler : IUnknown |
| 228 | +{ |
| 229 | + /// Called to provide the implementer with the completion status and result of |
| 230 | + /// the corresponding asynchronous method call. A failure errorCode will be |
| 231 | + /// passed if the content failed to load. Null means no content was found. |
| 232 | + /// Note content (if any) for redirect responses is ignored. |
| 233 | + HRESULT Invoke([in] HRESULT errorCode, [in] IStream* content); |
| 234 | +} |
| 235 | + |
| 236 | +} |
| 237 | +``` |
| 238 | + |
| 239 | +## WinRT |
| 240 | +```c# |
| 241 | +namespace Microsoft.Web.WebView2.Core |
| 242 | +{ |
| 243 | + // ... |
| 244 | +
|
| 245 | + runtimeclass CoreWebView2 |
| 246 | + { |
| 247 | + // ... |
| 248 | +
|
| 249 | + /// WebResourceResponseReceived is raised when the WebView receives the |
| 250 | + /// response for a request for a web resource (any URI resolution performed by |
| 251 | + /// the WebView; such as HTTP/HTTPS, file and data requests from redirects, |
| 252 | + /// navigations, declarations in HTML, implicit favicon lookups, and fetch API |
| 253 | + /// usage in the document). The host app can use this event to view the actual |
| 254 | + /// request and response for a web resource. There is no guarantee about the |
| 255 | + /// order in which the WebView processes the response and the host app's |
| 256 | + /// handler runs. The app's handler will not block the WebView from processing |
| 257 | + /// the response. |
| 258 | + event Windows.Foundation.TypedEventHandler<CoreWebView2, CoreWebView2WebResourceResponseReceivedEventArgs> WebResourceResponseReceived; |
| 259 | + } |
| 260 | + |
| 261 | + /// Event args for the WebResourceResponseReceived event. |
| 262 | + runtimeclass CoreWebView2WebResourceResponseReceivedEventArgs |
| 263 | + { |
| 264 | + /// The request object for the web resource, as committed. This includes |
| 265 | + /// headers added by the network stack that were not be included during the |
| 266 | + /// associated WebResourceRequested event, such as Authentication headers. |
| 267 | + /// Modifications to this object have no effect on how the request is |
| 268 | + /// processed as it has already been sent. |
| 269 | + CoreWebView2WebResourceRequest Request { get; }; |
| 270 | + /// View of the response object received for the web resource. |
| 271 | + CoreWebView2WebResourceResponseView Response { get; }; |
| 272 | + } |
| 273 | + |
| 274 | + /// View of the HTTP representation for a web resource response. The properties |
| 275 | + /// of this object are not mutable. This response view is used with the |
| 276 | + /// WebResourceResponseReceived event. |
| 277 | + runtimeclass CoreWebView2WebResourceResponseView |
| 278 | + { |
| 279 | + /// The HTTP response headers as received. |
| 280 | + CoreWebView2HttpResponseHeaders Headers { get; }; |
| 281 | + /// The HTTP response status code. |
| 282 | + Int32 StatusCode { get; }; |
| 283 | + /// The HTTP response reason phrase. |
| 284 | + String ReasonPhrase { get; }; |
| 285 | + /// Get the response content stream asynchronously. |
| 286 | + /// This method will throw a COM exception if the content failed to load. |
| 287 | + /// A null stream means no content was found. Note content (if any) for |
| 288 | + /// redirect responses is ignored. |
| 289 | + /// If this method is being called again before a first call has completed, |
| 290 | + /// it will complete at the same time all prior calls do. |
| 291 | + /// If this method is being called after a first call has completed, it will |
| 292 | + /// return immediately (asynchronously). |
| 293 | + Windows.Foundation.IAsyncOperation<Windows.Storage.Streams.IRandomAccessStream> GetContentAsync(); |
| 294 | + } |
| 295 | +} |
| 296 | +``` |
0 commit comments