{"id":2498,"date":"2016-12-09T10:22:20","date_gmt":"2016-12-08T22:22:20","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2498"},"modified":"2016-12-09T11:14:06","modified_gmt":"2016-12-08T23:14:06","slug":"retrofit-handling-different-responses","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2498\/","title":{"rendered":"Retrofit &#8211; Handling Different Responses"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">3<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>In my <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/2479\">previous post<\/a> I provided a simple example of how to use <a href=\"https:\/\/square.github.io\/retrofit\/\">Retrofit<\/a> to define, create and use a REST API client.  Even in that simple example the issue of how to deal with different responses to a request came up.  That is, where the response we receive does not conform to the strongly typed response we expected (or hoped for).  Here&#8217;s how we deal with that, in a strongly typed way.<\/p>\n<p><!--more--><\/p>\n<p>You may recall from my <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/2479\">previous post<\/a> that we defined a POPO type for the &#8220;happy&#8221; response to a request to the token end-point for logging in with the resource owner password flow.<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n    TokenResponse = public class\r\n    public\r\n      access_token: String;\r\n      refresh_token: String;\r\n      token_type: String;\r\n      expires_in: Integer;\r\n      username: String;\r\n\r\n      [SerializedName('as:client_id')]\r\n      client_id: String;\r\n\r\n      [SerializedName('.issued')]\r\n      issuedUTC: String;\r\n\r\n      [SerializedName('.expires')]\r\n      expiresUTC: String;\r\n    end;\r\n\r\n<\/pre>\n<p>This POPO represents the response to a successful login.<\/p>\n<p>In the handler for our response, we could access the response body in a strongly typed fashion using exactly this POPO class:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\n  method LoginActivity.onResponse(request: Call&lt;TokenResponse&gt;; response: Response&lt;TokenResponse&gt;);\r\n  begin\r\n    if response.code = 200 then\r\n    begin\r\n      \/\/ response.body is a TokenResponse.  i.e. an instance of the POPO we expected\r\n\r\n      var greeting := &quot;Hello &quot; + response.body.username;\r\n    end \r\n    else\r\n    ...\r\n  end;\r\n\r\n<\/pre>\n<p>But what if our request was unsuccessful ?  By which I mean we provided an invalid username or password or an incorrect client id or secret etc ?<\/p>\n<p>My back-end is an ASP.NET MVC WebApi in which I have implemented an OAuth 2.0 token provider.  A response to an invalid request is made by simply setting some error details on the OAuth Client Authentication context.  Here&#8217;s an excerpt to illustrate:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\n  method AuthorizationServerProvider.GrantResourceOwnerCredentials(context: OAuthGrantResourceOwnerCredentialsContext): Task;\r\n  begin\r\n    \/\/ ... details omitted for brevity ...\r\n\r\n    if user = nil then\r\n    begin\r\n      context.SetError('invalid_grant', 'The user name or password is invalid');\r\n      exit;\r\n    end;\r\n\r\n    \/\/ ... details omitted for brevity ...\r\n  end;\r\n\r\n<\/pre>\n<p>All similar errors are set in the same way.  In the event of a request completing in an error state, the .NET Oauth implementation will produce a JSON response similar to:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\r\n   {\r\n      &quot;error&quot;: &quot;invalid_grant&quot;,\r\n      &quot;error_description&quot;: &quot;The user name or password is invalid',\r\n      &quot;error_uri&quot;: null\r\n   } \r\n\r\n<\/pre>\n<p>So, in addition to the <strong>TokenResponse<\/strong>, I need another POPO.  Let&#8217;s call it <strong>TokenErrorResponse<\/strong>:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\n    TokenErrorResponse = public class\r\n    public\r\n      error: String;\r\n      error_description: String;\r\n      error_uri: String;\r\n    end;\r\n\r\n<\/pre>\n<p>So far so straightforward.  No need for any serialized name overrides.  The tricky bit comes next, although it&#8217;s not really <em>that<\/em> tricky.<\/p>\n<p>In the <strong>onResponse<\/strong> handler for the request, the <em>declared<\/em> response type (<strong>TokenResponse<\/strong>) is automatically represented on the response <strong>body<\/strong> property, in the case of a successful request.  But for <em>unsuccessful<\/em> requests we have not told the <strong>Retrofit<\/strong> what to expect (if anything).<\/p>\n<p>In that event <strong>Retrofit<\/strong> still provides access to any response body it may have received, and we access it in that case via the <strong>errorBody<\/strong> property of the response object in our handler.<\/p>\n<p>In the case of this particular client method, we know that the response will be one of those JSON error objects so we can get that in the form of a string:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\n  method LoginActivity.onResponse(request: Call&lt;TokenResponse&gt;; response: Response&lt;TokenResponse&gt;);\r\n  begin\r\n    if response.code = 200 then\r\n    begin\r\n      \/\/ ..\r\n    end \r\n    else\r\n    begin\r\n      var errorBody := response.errorBody.string;\r\n\r\n      \/\/ ...\r\n    end;\r\n  end;\r\n\r\n<\/pre>\n<p>In reality you probably wouldn&#8217;t simply assume that the response is a valid string and would do some additional checks before trying to work with the error body, but for the purposes of this exercise&#8230; Great!<\/p>\n<p>But now what do we do with it ?<\/p>\n<p>Simple:  We have declared a POPO to represent that error.  All we need to do is transform the JSON into an instance of that POPO, which is precisely what <strong>Gson<\/strong> is for.  All we need to do is get ourselves a suitable <strong>Gson<\/strong> instance and use it to transform the JSON string to an instance of <strong>TokenErrorResponse<\/strong> for us:<\/p>\n<pre class=\"brush: oxygene; title: ; notranslate\" title=\"\">\r\n\r\n      var errorBody := response.errorBody.string;\r\n      var gson      := (new GsonBuilder).setLenient.create;\r\n\r\n      var errorResponse := gson.fromJson(errorBody, typeOf(TokenErrorResponse));\r\n\r\n      \/\/ Now we can use the POPO to work with the error response\r\n\r\n      var message := &quot;Error: &quot; + errorResponse.error_description;\r\n\r\n<\/pre>\n<p>Similar techniques would obviously also work in the case of requests where even successful requests might return different responses under different circumstances.  In those cases you would implement a <strong>Retrofit<\/strong> with a method declared as returning a suitably general purpose response body type (e.g. String) and use <strong>Gson<\/strong> again as above, or some other object mapping technology as appropriate in the circumstances.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">3<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> In my previous post I provided a simple example of how to use Retrofit to define, create and use a REST API client. Even in that simple example the issue of how to deal with different responses to a request came up. That is, where the response we receive does not conform to the strongly [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[205,4,180],"tags":[310,181,308],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-Ei","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2479,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2479\/","url_meta":{"origin":2498,"position":0},"title":"Using Retrofit with Oxygene","date":"07 Dec 2016","format":false,"excerpt":"I've recently been working on a new project involving an Azure hosted ASP.NET MVC WebApi application (actually a pair of them) and native mobile and web applications. Everything is - of course - built using Oxygene. For the Android mobile app I was looking for a REST API client library\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2523,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2523\/","url_meta":{"origin":2498,"position":1},"title":"Anonymous Classes: Anonymous POCO&#8217;s","date":"14 Feb 2017","format":false,"excerpt":"There is another use case for anonymous classes, even simpler than that of providing implementations of interfaces: Anonymous POCO's. We're all familiar with the idea of declaring a class that identifies the members that all instances (objects) of that class have, and then creating instances of that class. Anonymous classes,\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/code-completion-java.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2574,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2574\/","url_meta":{"origin":2498,"position":2},"title":"Anonymous Classes: Identity Exposed!","date":"18 Feb 2017","format":false,"excerpt":"In my previous post on Anonymous Classes I erroneously referred to them as \"dynamic objects\" (thanks to commentors for pulling me up on that). Dynamic objects are something else entirely (although what precisely they might mean can vary on different platforms and in different languages). I have now corrected that\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1503,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1503\/","url_meta":{"origin":2498,"position":3},"title":"Sharing Code Across Platforms in Oxygene","date":"22 Aug 2013","format":false,"excerpt":"There seems to be a perception among some people that Delphi is in the unique position of allowing developers to share and re-use code across the various platforms that it's compiler can now (and will soon) target. But this is not the case. Oxygene has had this capability right from\u2026","rel":"","context":"In &quot;Cooper&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2470,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2470\/","url_meta":{"origin":2498,"position":4},"title":"Mix-in Unified and Segregated Syntax: An (Extended) Example","date":"25 Nov 2016","format":false,"excerpt":"Yesterday I initially posted that you couldn't mix Unified Syntax with \"traditional\" interface and implementation sections. Or what I am now calling Segregated Syntax. As sometimes happens, shortly after writing what I thought I knew to be true I discovered it wasn't ! Sorry about that. :) I promised to\u2026","rel":"","context":"In &quot;Cooper&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2240,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2240\/","url_meta":{"origin":2498,"position":5},"title":"Delayed POST Response","date":"11 Aug 2014","format":false,"excerpt":"Way back in September last year, Mason Wheeler blogged about his first experiences with developing for Android using Oxygene. I said at the time that I would look into reproducing his efforts and respond. I didn't get around to that right away and, as I think I have mentioned previously,\u2026","rel":"","context":"In &quot;Android&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2498"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=2498"}],"version-history":[{"count":6,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2498\/revisions"}],"predecessor-version":[{"id":2504,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2498\/revisions\/2504"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2498"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2498"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2498"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}