|
| 1 | += HAL-browser |
| 2 | + |
| 3 | +An API browser for the hal+json media type |
| 4 | + |
| 5 | +== Example Usage |
| 6 | + |
| 7 | +Here is an example of a hal+json API using the browser: |
| 8 | + |
| 9 | +http://haltalk.herokuapp.com/explorer/browser.html[http://haltalk.herokuapp.com/explorer/browser.html] |
| 10 | + |
| 11 | +== About HAL |
| 12 | + |
| 13 | +HAL is a format based on json that establishes conventions for |
| 14 | +representing links. For example: |
| 15 | + |
| 16 | +[source,javascript] |
| 17 | +---- |
| 18 | +{ |
| 19 | + "_links": { |
| 20 | + "self": { "href": "/orders" }, |
| 21 | + "next": { "href": "/orders?page=2" } |
| 22 | + } |
| 23 | +} |
| 24 | +---- |
| 25 | + |
| 26 | +More detail about HAL can be found at |
| 27 | +http://stateless.co/hal_specification.html[http://stateless.co/hal_specification.html]. |
| 28 | + |
| 29 | +== Customizing the POST form |
| 30 | + |
| 31 | +By default, the HAL Browser can't assume there is any metadata. When you click on the non-GET request button (to create a new resource), the user must enter the JSON document to submit. If your service includes metadata you can access, it's possible to plugin a custom view that makes use of it. |
| 32 | + |
| 33 | +. Define your custom view. |
| 34 | ++ |
| 35 | +Here is an example that leverages Spring Data REST's JSON Schema metadata found at */{entity}/schema*. |
| 36 | ++ |
| 37 | +[source,javascript] |
| 38 | +---- |
| 39 | +var CustomPostForm = Backbone.View.extend({ |
| 40 | + initialize: function (opts) { |
| 41 | + this.href = opts.href.split('{')[0]; |
| 42 | + this.vent = opts.vent; |
| 43 | + _.bindAll(this, 'createNewResource'); |
| 44 | + }, |
| 45 | +
|
| 46 | + events: { |
| 47 | + 'submit form': 'createNewResource' |
| 48 | + }, |
| 49 | +
|
| 50 | + className: 'modal fade', |
| 51 | +
|
| 52 | + createNewResource: function (e) { |
| 53 | + e.preventDefault(); |
| 54 | +
|
| 55 | + var self = this; |
| 56 | +
|
| 57 | + var data = {} |
| 58 | + Object.keys(this.schema.properties).forEach(function(property) { |
| 59 | + if (!("format" in self.schema.properties[property])) { |
| 60 | + data[property] = self.$('input[name=' + property + ']').val(); |
| 61 | + } |
| 62 | + }); |
| 63 | +
|
| 64 | + var opts = { |
| 65 | + url: this.$('.url').val(), |
| 66 | + headers: HAL.parseHeaders(this.$('.headers').val()), |
| 67 | + method: this.$('.method').val(), |
| 68 | + data: JSON.stringify(data) |
| 69 | + }; |
| 70 | +
|
| 71 | + var request = HAL.client.request(opts); |
| 72 | + request.done(function (response) { |
| 73 | + self.vent.trigger('response', {resource: response, jqxhr: jqxhr}); |
| 74 | + }).fail(function (response) { |
| 75 | + self.vent.trigger('fail-response', {jqxhr: jqxhr}); |
| 76 | + }).always(function () { |
| 77 | + self.vent.trigger('response-headers', {jqxhr: jqxhr}); |
| 78 | + window.location.hash = 'NON-GET:' + opts.url; |
| 79 | + }); |
| 80 | +
|
| 81 | + this.$el.modal('hide'); |
| 82 | + }, |
| 83 | +
|
| 84 | + render: function (opts) { |
| 85 | + var headers = HAL.client.getHeaders(); |
| 86 | + var headersString = ''; |
| 87 | +
|
| 88 | + _.each(headers, function (value, name) { |
| 89 | + headersString += name + ': ' + value + '\n'; |
| 90 | + }); |
| 91 | +
|
| 92 | + var request = HAL.client.request({ |
| 93 | + url: this.href + '/schema', |
| 94 | + method: 'GET' |
| 95 | + }); |
| 96 | +
|
| 97 | + var self = this; |
| 98 | + request.done(function (schema) { |
| 99 | + self.schema = schema; |
| 100 | + self.$el.html(self.template({ |
| 101 | + href: self.href, |
| 102 | + schema: self.schema, |
| 103 | + user_defined_headers: headersString})); |
| 104 | + self.$el.modal(); |
| 105 | + }); |
| 106 | +
|
| 107 | + return this; |
| 108 | + }, |
| 109 | + template: _.template($('#dynamic-request-template').html()) |
| 110 | +}); |
| 111 | +---- |
| 112 | ++ |
| 113 | +. Register it by assigning to `HAL.customPostForm` |
| 114 | ++ |
| 115 | +[source,javascript] |
| 116 | +---- |
| 117 | +HAL.customPostForm = CustomPostForm; |
| 118 | +---- |
| 119 | ++ |
| 120 | +. Load your custom JavaScript component and define your custom HTML template. |
| 121 | ++ |
| 122 | +[source,html,indent=0] |
| 123 | +---- |
| 124 | +<script id="dynamic-request-template" type="text/template"> |
| 125 | +<div class="modal-header"> |
| 126 | + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
| 127 | + <h3>Make a non-GET request</h3> |
| 128 | +</div> |
| 129 | +
|
| 130 | +<form class="non-safe" action="<%= href %>"> |
| 131 | + <div class="modal-body"> |
| 132 | + <p>Target URI</p> |
| 133 | + <input name="url" type="text" class="url" value="<%= href %>" /> |
| 134 | + <p>Method:</p> |
| 135 | + <input name="method" type="text" class="method" value="POST" /> |
| 136 | + <p>Headers:</p> |
| 137 | + <textarea name="headers" class="headers" style="height: 100px"> |
| 138 | + Content-Type: application/json |
| 139 | + <%= user_defined_headers %> |
| 140 | + </textarea> |
| 141 | + </div> |
| 142 | + <% _.each(schema.properties, function(value, name) { %> |
| 143 | + <% if (!("format" in value)) { %> |
| 144 | + <input type="text" placeholder="<%= name %>" name="<%= name %>" /> |
| 145 | + <% } %> |
| 146 | + <% }); %> |
| 147 | + <div class="modal-footer"> |
| 148 | + <button type="submit" class="btn btn-primary">Make Request</button> |
| 149 | + </div> |
| 150 | +</form> |
| 151 | +</script> |
| 152 | +---- |
| 153 | + |
| 154 | +NOTE: To load a custom JavaScript module AND a custom HTML template, you will probably need to create a customized version of `browser.html`. |
| 155 | + |
| 156 | +NOTE: The HAL Browser uses a global `HAL` object, so there is no need to deal with JavaScript packages. |
| 157 | + |
| 158 | +== Usage Instructions |
| 159 | + |
| 160 | +All you should need to do is copy the files into your webroot. |
| 161 | +It is OK to put it in a subdirectory; it does not need to be in the root. |
| 162 | + |
| 163 | +All the JS and CSS dependencies come included in the vendor directory. |
| 164 | + |
| 165 | +== TODO |
| 166 | + |
| 167 | +* Provide feedback to user when there are issues with response (missing |
| 168 | +self link, wrong media type identifier) |
| 169 | +* Give 'self' and 'curies' links special treatment |
0 commit comments