blix-rest 0.9.3 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +484 -503
- data/lib/blix/rest/cache.rb +1 -29
- data/lib/blix/rest/controller.rb +80 -0
- data/lib/blix/rest/memory_cache.rb +30 -0
- data/lib/blix/rest/request_mapper.rb +36 -28
- data/lib/blix/rest/response.rb +10 -0
- data/lib/blix/rest/server.rb +57 -51
- data/lib/blix/rest/session.rb +8 -3
- data/lib/blix/rest/version.rb +1 -1
- data/lib/blix/rest.rb +7 -0
- data/lib/blix/utils/misc.rb +39 -0
- metadata +5 -9
- data/lib/blix/rest/cucumber/hooks.rb +0 -5
- data/lib/blix/rest/cucumber/request_steps.rb +0 -207
- data/lib/blix/rest/cucumber/resource_steps.rb +0 -28
- data/lib/blix/rest/cucumber/world.rb +0 -274
- data/lib/blix/rest/cucumber.rb +0 -8
data/README.md
CHANGED
@@ -1,504 +1,521 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
Blix::Rest User Guide
|
2
|
+
=====================
|
3
|
+
|
4
|
+
Table of Contents
|
5
|
+
-----------------
|
6
|
+
|
7
|
+
- [Blix::Rest User Guide](#blixrest-user-guide)
|
8
|
+
- [Table of Contents](#table-of-contents)
|
9
|
+
- [Introduction](#introduction)
|
10
|
+
- [Installation](#installation)
|
11
|
+
- [Creating a Simple Web Service](#creating-a-simple-web-service)
|
12
|
+
- [Controllers](#controllers)
|
13
|
+
- [Hooks](#hooks)
|
14
|
+
- [Routing](#routing)
|
15
|
+
- [Path Parameters](#path-parameters)
|
16
|
+
- [Query Parameters](#query-parameters)
|
17
|
+
- [Body Values](#body-values)
|
18
|
+
- [Wildcard Paths](#wildcard-paths)
|
19
|
+
- [Path Options](#path-options)
|
20
|
+
- [Request Handling](#request-handling)
|
21
|
+
- [Request Format](#request-format)
|
22
|
+
- [Response Handling](#response-handling)
|
23
|
+
- [Setting Headers and Status](#setting-headers-and-status)
|
24
|
+
- [Generating Error Responses](#generating-error-responses)
|
25
|
+
- [Predefined and Custom Formats](#predefined-and-custom-formats)
|
26
|
+
- [Custom Responses](#custom-responses)
|
27
|
+
- [Handling All Paths and Preserving File Extensions](#handling-all-paths-and-preserving-file-extensions)
|
28
|
+
- [Authentication](#authentication)
|
29
|
+
- [Sessions](#sessions)
|
30
|
+
- [Caching](#caching)
|
31
|
+
- [CORS (Cross-Origin Resource Sharing)](#cors-cross-origin-resource-sharing)
|
32
|
+
- [Rate Limiting](#rate-limiting)
|
33
|
+
- [Views](#views)
|
34
|
+
- [directory structure](#directory-structure)
|
35
|
+
- [Logging](#logging)
|
36
|
+
- [Testing with Cucumber](#testing-with-cucumber)
|
37
|
+
- [Asset Management](#asset-management)
|
38
|
+
- [Controller Helper Methods](#controller-helper-methods)
|
39
|
+
|
40
|
+
<div style="page-break-after: always;"></div>
|
41
|
+
|
42
|
+
Introduction
|
43
|
+
------------
|
44
|
+
|
45
|
+
Blix::Rest is a web application framework for Ruby that provides a
|
46
|
+
simple and flexible way to build web services, APIs, and web
|
47
|
+
applications. It supports RESTful routing, customizable controllers, and
|
48
|
+
various formats for responses.
|
49
|
+
|
50
|
+
Installation
|
51
|
+
------------
|
52
|
+
|
53
|
+
To install Blix::Rest, use the following command:
|
54
|
+
|
55
|
+
```bash
|
3
56
|
gem install blix-rest
|
57
|
+
```
|
4
58
|
|
59
|
+
Creating a Simple Web Service
|
60
|
+
-----------------------------
|
5
61
|
|
6
|
-
|
7
|
-
|
8
|
-
#### put the following in config.ru
|
62
|
+
To create a simple web service, follow these steps:
|
9
63
|
|
64
|
+
1\. Create a new file named `config.ru` with the following content:
|
10
65
|
|
66
|
+
```ruby
|
11
67
|
require 'blix/rest'
|
12
68
|
|
13
69
|
class HomeController < Blix::Rest::Controller
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
70
|
+
get '/hello', :accept => [:html, :json], :default => :html do
|
71
|
+
if format == :json
|
72
|
+
{"message" => "hello world"}
|
73
|
+
else
|
74
|
+
"<h1>hello world</h1>"
|
75
|
+
end
|
76
|
+
end
|
22
77
|
end
|
23
78
|
|
24
79
|
run Blix::Rest::Server.new
|
80
|
+
```
|
25
81
|
|
82
|
+
or at its simplest, only accepting the default `:json` format:
|
26
83
|
|
84
|
+
```ruby
|
85
|
+
require 'blix/rest'
|
27
86
|
|
87
|
+
class HomeController < Blix::Rest::Controller
|
88
|
+
get '/hello', do
|
89
|
+
{"message" => "hello world"}
|
90
|
+
end
|
91
|
+
end
|
28
92
|
|
29
|
-
|
30
|
-
|
31
|
-
`ruby -S rackup -p3000`
|
32
|
-
|
33
|
-
|
34
|
-
#### now go to your browser and enter ..
|
35
|
-
|
36
|
-
`http://localhost:3000/hello`
|
37
|
-
|
38
|
-
or
|
39
|
-
|
40
|
-
`http://localhost:3000/hello.json`
|
41
|
-
|
42
|
-
## Note on JSON
|
43
|
-
|
44
|
-
the default json parser uses multi json. load the specific json library you need
|
45
|
-
before loading `blix/rest`.
|
46
|
-
|
47
|
-
when using oj then you may need to set some default options eg:
|
48
|
-
|
49
|
-
`MultiJson.default_options = {:mode=>:custom, :use_as_json=>true}`
|
50
|
-
|
51
|
-
## NOTE ON PATHS
|
52
|
-
|
53
|
-
`get '/user/:user_id/list`
|
54
|
-
|
55
|
-
`path_params[:user_id]` contains the content of the path at location :user_id
|
56
|
-
|
57
|
-
`get '/resource/*wildpath'`
|
58
|
-
|
59
|
-
`path_params[:wildpath]` contains the remainder of the path where the * is.
|
60
|
-
|
61
|
-
if there is a more specific path then it will be used first :
|
62
|
-
|
63
|
-
`get '/resource/aaa'` will be used before `get '/resource/*'`
|
64
|
-
|
65
|
-
`get '/*'` will be used as a default path if no other paths match.
|
66
|
-
|
67
|
-
`all '/mypath'` will accept all http_methods but if a more specific handler
|
68
|
-
is specified then it will be used first.
|
69
|
-
|
70
|
-
|
71
|
-
### Path options
|
72
|
-
|
73
|
-
:accept : the format or formats to accept eg: :html or [:png, :jpeg]
|
74
|
-
:default : default format if not derived through other means.
|
75
|
-
:force : force response into the given format
|
76
|
-
:query : derive format from request query (default: false)
|
77
|
-
:extension : derive format from path extension (default: true)
|
78
|
-
|
79
|
-
|
80
|
-
use `:accept=>:*` in combination with `:force` to accept all request formats.
|
81
|
-
|
82
|
-
## APPLICATION MOUNT POINT
|
83
|
-
|
84
|
-
this is the path of the mount path of the application
|
85
|
-
|
86
|
-
this will be set to the environment variable `BLIX_REST_ROOT` if present
|
87
|
-
|
88
|
-
otherwise set it manually with:
|
89
|
-
|
90
|
-
`Blix::Rest.set_path_root( "/myapplication")`
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
## GENERATE AN ERROR RESPONSE
|
96
|
-
|
97
|
-
`send_error(message,status,headers)`
|
98
|
-
|
99
|
-
or for standard headers and status 406 just ..
|
100
|
-
|
101
|
-
`send_error "my error message`
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
## HEADERS && STATUS
|
106
|
-
|
107
|
-
add special headers to your response with eg:
|
108
|
-
|
109
|
-
`add_headers( "AAA"=>"xxx","BBB"=>"yyyy")`
|
93
|
+
run Blix::Rest::Server.new
|
94
|
+
```
|
110
95
|
|
111
|
-
change the status of a success response with eg:
|
112
96
|
|
113
|
-
`set_status(401)`
|
114
97
|
|
98
|
+
2\. Start the server:
|
115
99
|
|
116
|
-
|
100
|
+
```
|
101
|
+
ruby -S rackup -p3000
|
102
|
+
```
|
117
103
|
|
118
|
-
|
119
|
-
|
104
|
+
3\. Access the service:
|
105
|
+
* For HTML: `http://localhost:3000/hello`
|
106
|
+
* For JSON: `http://localhost:3000/hello.json`
|
120
107
|
|
121
|
-
|
122
|
-
|
108
|
+
Controllers
|
109
|
+
-----------
|
123
110
|
|
124
|
-
|
111
|
+
Controllers in Blix::Rest inherit from `Blix::Rest::Controller`. They
|
112
|
+
provide various methods and hooks for handling requests and responses.
|
125
113
|
|
126
|
-
|
114
|
+
### Hooks
|
127
115
|
|
128
|
-
|
116
|
+
Controllers support `before`, `before_route`, and `after` hooks:
|
129
117
|
|
130
|
-
|
131
|
-
|
118
|
+
```ruby
|
119
|
+
class MyController < Blix::Rest::Controller
|
132
120
|
|
121
|
+
before do
|
122
|
+
# Code to run before each request
|
123
|
+
end
|
133
124
|
|
125
|
+
before_route do
|
126
|
+
# Code to run after 'before' but before the route is executed
|
127
|
+
end
|
134
128
|
|
129
|
+
after do
|
130
|
+
# Code to run after each request
|
131
|
+
end
|
132
|
+
end
|
133
|
+
```
|
135
134
|
|
136
|
-
|
135
|
+
- `before`: Runs before any request processing begins. Use this for
|
136
|
+
setup operations that should occur for all routes in the controller.
|
137
|
+
- `before_route`: Runs after `before` but before the specific route
|
138
|
+
action is executed. This is useful for operations that should occur
|
139
|
+
for all routes but may depend on request-specific information.
|
140
|
+
- `after`: Runs after the request has been processed. Use this for
|
141
|
+
cleanup operations or final modifications to the response.
|
137
142
|
|
138
|
-
|
139
|
-
|
143
|
+
These hooks allow you to execute code at different stages of the request
|
144
|
+
lifecycle, providing flexibility in how you handle requests and
|
145
|
+
responses.
|
140
146
|
|
141
|
-
|
142
|
-
the extension part ( after the .) of the url ... eg
|
147
|
+
### Routing
|
143
148
|
|
144
|
-
|
149
|
+
Blix::Rest supports various HTTP methods for routing:
|
145
150
|
|
146
|
-
|
147
|
-
|
151
|
+
```ruby
|
152
|
+
class MyController < Blix::Rest::Controller
|
148
153
|
|
154
|
+
get '/users' do
|
155
|
+
# Handle GET request
|
156
|
+
end
|
149
157
|
|
150
|
-
|
158
|
+
post '/users' do
|
159
|
+
# Handle POST request
|
160
|
+
end
|
151
161
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
headers[CONTENT_TYPE] = CONTENT_TYPE_JSONP
|
156
|
-
end
|
162
|
+
put '/users/:id' do
|
163
|
+
# Handle PUT request
|
164
|
+
end
|
157
165
|
|
158
|
-
|
159
|
-
|
160
|
-
|
166
|
+
delete '/users/:id' do
|
167
|
+
# Handle DELETE request
|
168
|
+
end
|
161
169
|
|
162
|
-
def format_response(value,response)
|
163
|
-
response.content = "<script>load(" +
|
164
|
-
MultiJson.dump( value) +
|
165
|
-
")</script>"
|
166
|
-
end
|
167
170
|
end
|
171
|
+
```
|
168
172
|
|
173
|
+
### Path Parameters
|
169
174
|
|
170
|
-
|
171
|
-
s.register_parser(:jsonp,MyParser.new)
|
172
|
-
|
173
|
-
|
174
|
-
Then in your controller accept that format..
|
175
|
+
You can use path parameters in your routes:
|
175
176
|
|
176
|
-
|
177
|
-
get
|
178
|
-
|
177
|
+
```ruby
|
178
|
+
get '/user/:user_id/list' do
|
179
|
+
user_id = path_params[:user_id]
|
180
|
+
# Use user_id
|
179
181
|
end
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
182
|
+
```
|
183
|
+
|
184
|
+
### Query Parameters
|
185
|
+
|
186
|
+
Query parameters are key-value pairs that appear after the question mark (?) in a URL.
|
187
|
+
You can access query parameters in your routes like this:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
get '/search' do
|
191
|
+
query = query_params[:q]
|
192
|
+
limit = query_params[:limit]&.to_i || 10
|
193
|
+
# Use query and limit in your search logic
|
194
|
+
{ results: perform_search(query, limit) }
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
198
|
+
### Body Values
|
199
|
+
|
200
|
+
There are several methods to access data from the request body, particularly useful for POST, PUT, and PATCH requests. Here are the main ways to access body values:
|
201
|
+
|
202
|
+
1. `body_hash`: This method parses the request body and returns it as a hash. It's particularly useful for JSON payloads.
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
post '/users' do
|
206
|
+
user_data = body_hash
|
207
|
+
new_user = User.create(user_data)
|
208
|
+
{ id: new_user.id, message: "User created successfully" }
|
209
|
+
end
|
210
|
+
```
|
211
|
+
|
212
|
+
2. `body`: This method returns the raw request body as a string. It's useful when you need to process the raw data yourself.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
post '/raw-data' do
|
216
|
+
raw_data = body
|
217
|
+
# Process the raw data
|
218
|
+
{ received_bytes: raw_data.bytesize }
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
222
|
+
3. `form_hash`: This method returns a hash of form data from POST requests. It's particularly useful for handling form submissions.
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
post '/submit-form' do
|
226
|
+
form_data = form_hash
|
227
|
+
# Process the form data
|
228
|
+
{ message: "Form received", name: form_data['name'] }
|
229
|
+
end
|
230
|
+
```
|
231
|
+
|
232
|
+
4. `get_data(field)`: This method allows you to get a specific field from the request body's data hash.
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
put '/users/:id' do
|
236
|
+
user_id = path_params[:id]
|
237
|
+
new_name = get_data('name')
|
238
|
+
# Update user name
|
239
|
+
{ message: "User #{user_id} updated with name #{new_name}" }
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
When working with body values, keep these points in mind:
|
244
|
+
|
245
|
+
- The appropriate method to use depends on the `Content-Type` of the request.
|
246
|
+
- For JSON payloads, `body_hash` automatically parses the JSON into a Ruby hash.
|
247
|
+
- For form submissions, `form_hash` is the most convenient method.
|
248
|
+
- Always validate and sanitize input data before using it in your application logic.
|
249
|
+
- If the body cannot be parsed (e.g., invalid JSON), these methods may return nil or raise an error, so consider adding error handling.
|
250
|
+
|
251
|
+
### Wildcard Paths
|
252
|
+
|
253
|
+
You can use wildcard paths:
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
get '/resource/*wildpath' do
|
257
|
+
wildpath = path_params[:wildpath]
|
258
|
+
# Use wildpath
|
193
259
|
end
|
260
|
+
```
|
194
261
|
|
262
|
+
### Path Options
|
195
263
|
|
196
|
-
|
197
|
-
|
198
|
-
add_headers 'Content-Type'=>'text/xyz'
|
199
|
-
raise RawResponse, 'xyz'
|
200
|
-
|
201
|
-
or with status and headers:
|
202
|
-
|
203
|
-
raise RawResponse.new('xyz', 123, 'Content-Type'=>'text/xyz')
|
264
|
+
You can specify various options for your routes:
|
204
265
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
2. the request query `format` parameter if the `:query` option is true
|
212
|
-
|
213
|
-
3. the url extension unless the `:extension` option is false.
|
214
|
-
|
215
|
-
4. the accept header format
|
266
|
+
```ruby
|
267
|
+
get '/users', :accept => [:html, :json], :default => :html do
|
268
|
+
# Handle request
|
269
|
+
end
|
270
|
+
```
|
216
271
|
|
217
|
-
|
272
|
+
Available options:
|
218
273
|
|
219
|
-
|
274
|
+
- `:accept`: Formats to accept (e.g., `:html`, `[:png, :jpeg]`, `:*`)
|
275
|
+
- `:default`: Default format if not specified
|
276
|
+
- `:force`: Force response into a given format
|
277
|
+
- `:query`: Derive format from request query (default: false)
|
278
|
+
- `:extension`: Derive format from path extension (default: true)
|
220
279
|
|
280
|
+
Request Handling
|
281
|
+
----------------
|
221
282
|
|
222
|
-
|
283
|
+
### Request Format
|
223
284
|
|
224
|
-
|
285
|
+
The format of a request is derived in the following order:
|
225
286
|
|
226
|
-
|
227
|
-
|
287
|
+
1. The `:force` option value if present
|
288
|
+
2. The request query `format` parameter if the `:query` option is true
|
289
|
+
3. The URL extension unless the `:extension` option is false
|
290
|
+
4. The accept header format
|
291
|
+
5. The format specified in the `:default` option
|
292
|
+
6. `:json` (default)
|
228
293
|
|
294
|
+
Response Handling
|
295
|
+
-----------------
|
229
296
|
|
230
|
-
|
231
|
-
method : the request method lowercase( 'get'/'post' ..)
|
232
|
-
req : the rack request
|
233
|
-
body : the request body as a string
|
234
|
-
path : the request path
|
235
|
-
query_params : a hash of parameters as passed in the url as parameters
|
236
|
-
path_params : a hash of parameters constructed from variable parts of the path
|
237
|
-
post_params : a hash of parameters passed in the body of the request
|
238
|
-
params : all the params combined
|
239
|
-
allow_methods : allow the non standard http verbs in the controller. eg `:propfind `
|
240
|
-
user : the user making this request ( or nil if
|
241
|
-
format : the format the response should be in :json or :html
|
242
|
-
before : before hook ( opts ) - remember to add 'super' as first line !!!
|
243
|
-
after : after hook (opts,response)- remember to add 'super' as first line !!!
|
244
|
-
proxy : forward the call to another service (service,path, opts={}) , :include_query=>true/false
|
245
|
-
session : req.session
|
246
|
-
set_status : set the http response status ( 200 )
|
247
|
-
redirect : (path, status=302) redirect to another url.
|
248
|
-
request_ip : the ip of the request
|
249
|
-
render_erb : (template_name [,:layout=>name])
|
250
|
-
server_cache : get the server cache object
|
251
|
-
server_cache_get : retrieve/store value in cache
|
252
|
-
path_for : (path) give the external path for an internal path
|
253
|
-
url_for : (path) give the full url for an internal path
|
254
|
-
h : escape html string to avoid XSS
|
255
|
-
escape_javascript : escape a javascript string
|
256
|
-
server_options : options as passed to server at create time
|
257
|
-
logger : system logger
|
258
|
-
mode_test? : test mode ?
|
259
|
-
mode_production? : production mode ?
|
260
|
-
mode_development? : development mode?
|
261
|
-
send_data : send raw data (data, options )
|
262
|
-
[:type=>mimetype]
|
263
|
-
[:filename=>name]
|
264
|
-
[:disposition=>inline|attachment]
|
265
|
-
[:status=>234]
|
297
|
+
### Setting Headers and Status
|
266
298
|
|
267
|
-
|
268
|
-
|
299
|
+
```ruby
|
300
|
+
add_headers("X-Custom-Header" => "Value")
|
301
|
+
set_status(201)
|
302
|
+
```
|
269
303
|
|
270
|
-
|
304
|
+
### Generating Error Responses
|
271
305
|
|
272
|
-
|
306
|
+
```ruby
|
307
|
+
send_error("Error message", 400)
|
308
|
+
```
|
273
309
|
|
274
|
-
###
|
310
|
+
### Predefined and Custom Formats
|
275
311
|
|
276
|
-
|
277
|
-
|
278
|
-
|
312
|
+
Blix::Rest comes with predefined formats such as `:json`, `:html`,
|
313
|
+
`:xml`, and others. However, you can also register custom formats to
|
314
|
+
handle specific content types.
|
279
315
|
|
280
|
-
|
316
|
+
To register a new format:
|
281
317
|
|
282
|
-
|
283
|
-
|
318
|
+
```ruby
|
319
|
+
class MyCustomFormatParser < Blix::Rest::FormatParser
|
320
|
+
def initialize
|
321
|
+
super(:mycustom, 'application/x-mycustom')
|
284
322
|
end
|
285
323
|
|
286
|
-
|
287
|
-
|
324
|
+
def parse(text)
|
325
|
+
# Custom parsing logic here
|
288
326
|
end
|
289
327
|
|
328
|
+
def format(obj)
|
329
|
+
# Custom formatting logic here
|
330
|
+
end
|
290
331
|
end
|
291
332
|
|
333
|
+
Blix::Rest::Server.register_parser(MyCustomFormatParser.new)
|
334
|
+
```
|
292
335
|
|
293
|
-
|
336
|
+
After registering your custom format parser, you can use it in your
|
337
|
+
routes:
|
294
338
|
|
295
|
-
|
339
|
+
```ruby
|
340
|
+
get '/custom', :accept => :mycustom do
|
341
|
+
# Your custom format will be used for the response
|
342
|
+
{ data: 'Some data' }
|
343
|
+
end
|
344
|
+
```
|
296
345
|
|
297
|
-
the verb can not be modified
|
298
346
|
|
299
|
-
|
347
|
+
### Custom Responses
|
300
348
|
|
301
|
-
|
349
|
+
To return a custom response without using a registered format parser,
|
350
|
+
use the `:force => :raw` option:
|
302
351
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
...
|
352
|
+
```ruby
|
353
|
+
get '/custom', :accept => :xyz, :force => :raw do
|
354
|
+
add_headers 'Content-Type' => 'text/xyz'
|
355
|
+
"Custom response"
|
308
356
|
end
|
357
|
+
```
|
358
|
+
|
359
|
+
This approach allows you to have full control over the response format
|
360
|
+
and headers.
|
309
361
|
|
310
|
-
|
362
|
+
### Handling All Paths and Preserving File Extensions
|
311
363
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
path_prefix('/xx') # ensure the path has the given prefix
|
316
|
-
default_option(:xxx,'foo') # ensure that option has the given default value
|
364
|
+
In some cases, you might want to respond to all paths and keep the file
|
365
|
+
extension as part of the path, rather than using it to determine the
|
366
|
+
response format. You can achieve this by using a combination of options:
|
317
367
|
|
318
|
-
|
368
|
+
```ruby
|
369
|
+
get '/*path', :accept => :*, :extension => false, :force => :raw do
|
370
|
+
file_path = path_params[:path]
|
371
|
+
# Handle the request based on the full path, including any file extension
|
372
|
+
content = read_file(file_path)
|
373
|
+
content_type = determine_content_type(file_path)
|
319
374
|
|
320
|
-
|
375
|
+
add_headers 'Content-Type' => content_type
|
376
|
+
content
|
377
|
+
end
|
378
|
+
```
|
379
|
+
|
380
|
+
In this example:
|
321
381
|
|
322
|
-
|
382
|
+
- `:accept => :*` allows the route to accept any content type.
|
383
|
+
- `:extension => false` prevents Blix::Rest from using the file
|
384
|
+
extension to determine the response format.
|
385
|
+
- `:force => :raw` gives you full control over the response, including
|
386
|
+
setting the appropriate Content-Type header.
|
323
387
|
|
324
|
-
|
388
|
+
This configuration is particularly useful when you're serving static
|
389
|
+
files or when you need to preserve the original path structure in your
|
390
|
+
application logic.
|
325
391
|
|
326
|
-
|
392
|
+
Authentication
|
393
|
+
--------------
|
327
394
|
|
328
|
-
|
395
|
+
Blix::Rest supports basic authentication:
|
329
396
|
|
330
|
-
|
397
|
+
```ruby
|
398
|
+
login, password = get_basic_auth
|
399
|
+
auth_error("Invalid login or password") unless valid_credentials?(login, password)
|
400
|
+
```
|
331
401
|
|
332
|
-
|
333
|
-
|
334
|
-
:samesite =>:strict # use strict x-site policy
|
335
|
-
:samesite =>:lax # use lax x-site policy
|
402
|
+
Sessions
|
403
|
+
--------
|
336
404
|
|
337
|
-
|
405
|
+
Blix::Rest provides session management:
|
338
406
|
|
407
|
+
```ruby
|
339
408
|
require 'blix/utils/redis_store'
|
340
409
|
require 'blix/rest/session'
|
341
410
|
|
342
411
|
class MyController < Blix::Rest::Controller
|
343
412
|
include Blix::Rest::Session
|
344
413
|
|
345
|
-
session_name :
|
346
|
-
session_opts :http=>true
|
347
|
-
session_manager
|
348
|
-
|
414
|
+
session_name :my_session
|
415
|
+
session_opts :http => true
|
416
|
+
session_manager MySessionManager.new
|
349
417
|
|
350
|
-
def
|
351
|
-
|
352
|
-
|
353
|
-
session['yyy'] = true
|
418
|
+
def my_action
|
419
|
+
session['user_id'] = 123
|
420
|
+
@user_data = session['user_data']
|
354
421
|
end
|
355
|
-
|
356
422
|
end
|
423
|
+
```
|
357
424
|
|
358
|
-
|
359
|
-
|
360
|
-
:secure # false
|
361
|
-
:http # false
|
362
|
-
:samesite # lax
|
363
|
-
:path # '/'
|
364
|
-
:expire_secs # 30 mins
|
365
|
-
:cleanup_every_secs # 5 minutes
|
366
|
-
:max_age # nil
|
367
|
-
|
368
|
-
the following methods are available:
|
369
|
-
|
370
|
-
reset_session # gen new session id
|
371
|
-
session # session hash
|
372
|
-
csrf_token # the session csrf token
|
373
|
-
|
374
|
-
route options that affect sessions:
|
375
|
-
|
376
|
-
:nosession=>true # no session will be set/retrieved
|
377
|
-
:cache=>true # no session will be set/retrieved
|
378
|
-
:csrf=>true # request will be validated for calid csrf token.
|
379
|
-
# token must be in header X_CSRF_TOKEN field
|
380
|
-
|
381
|
-
session configuration is inherited from superclass controllers unless overridden.
|
382
|
-
|
383
|
-
## Cache
|
384
|
-
|
385
|
-
|
386
|
-
the server has a cache which can also be used for storing your own data.
|
387
|
-
|
388
|
-
within a controller access the controller with `server_cache` which returns the
|
389
|
-
cache object.
|
425
|
+
Caching
|
426
|
+
-------
|
390
427
|
|
391
|
-
|
428
|
+
Blix::Rest provides a caching mechanism:
|
392
429
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
delete(key) # delete a key from the cache
|
397
|
-
clear # delete all keys from the cache.
|
430
|
+
```ruby
|
431
|
+
value = server_cache_get('my_key') { expensive_operation() }
|
432
|
+
```
|
398
433
|
|
399
|
-
|
434
|
+
To cache responses automatically, add `:cache => true` to your route
|
435
|
+
options.
|
400
436
|
|
401
|
-
|
437
|
+
CORS (Cross-Origin Resource Sharing)
|
438
|
+
------------------------------------
|
402
439
|
|
403
|
-
|
404
|
-
the action in the provided block and store the result in the cache.
|
440
|
+
To enable CORS for a route:
|
405
441
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
..
|
412
|
-
end
|
413
|
-
|
414
|
-
def set(key,value)
|
415
|
-
..
|
416
|
-
end
|
417
|
-
|
418
|
-
def key?(key)
|
419
|
-
..
|
420
|
-
end
|
421
|
-
|
422
|
-
def delete(key)
|
423
|
-
..
|
424
|
-
end
|
425
|
-
|
426
|
-
def clear
|
427
|
-
..
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
cache = MyCache.new
|
432
|
-
|
433
|
-
app = Blix::Rest::Server.new(:cache=>cache)
|
434
|
-
|
435
|
-
there is a redis cache already defined:
|
436
|
-
|
437
|
-
require 'blix/rest/redis_cache'
|
442
|
+
```ruby
|
443
|
+
get '/api/data' do
|
444
|
+
set_accept_cors
|
445
|
+
{ data: 'Some data' }
|
446
|
+
end
|
438
447
|
|
439
|
-
|
440
|
-
|
448
|
+
options '/api/data' do
|
449
|
+
set_accept_cors(
|
450
|
+
:origin => 'https://example.com',
|
451
|
+
:methods => [:get, :post],
|
452
|
+
:headers => ['Content-Type', 'Authorization']
|
453
|
+
)
|
454
|
+
end
|
455
|
+
```
|
441
456
|
|
457
|
+
Rate Limiting
|
458
|
+
-------------
|
442
459
|
|
443
|
-
|
460
|
+
Blix::Rest provides a rate limiting mechanism, only allow so many exceptions in a given time:
|
444
461
|
|
445
|
-
|
462
|
+
the delay times are applied to:
|
446
463
|
|
447
|
-
|
448
|
-
|
464
|
+
* 3x failure
|
465
|
+
* 10x failure
|
466
|
+
* 100x failure
|
449
467
|
|
450
|
-
|
468
|
+
```ruby
|
469
|
+
rate_limit('api_calls', times: [60, 600, 86400]) do
|
470
|
+
# Your rate-limited code here. If an exception ir raised
|
471
|
+
# then the failure count is incremented and an exception is raised
|
472
|
+
# until the corresponding delay is expired.
|
473
|
+
end
|
474
|
+
```
|
451
475
|
|
452
|
-
|
476
|
+
the `api_calls` here is a key which is used to store the failure count against.
|
477
|
+
If you were rate limiting a login then you might use the user name as part of this key.
|
453
478
|
|
454
|
-
|
479
|
+
The `times:` array specifies the rate limiting intervals in seconds. The default values are the
|
480
|
+
same as in this example:
|
455
481
|
|
456
|
-
|
482
|
+
- `60`: Limits requests per minute (60 seconds)
|
483
|
+
- `600`: Limits requests per 10 minutes (600 seconds, which is 10 minutes)
|
484
|
+
- `86400`: Limits requests per day (86400 seconds, which is 24 hours)
|
457
485
|
|
458
|
-
cross origin site requests
|
459
486
|
|
460
|
-
MyController < Blix::Rest::Controller
|
461
487
|
|
462
|
-
|
463
|
-
|
464
|
-
{:data=>'foo'}
|
465
|
-
end
|
488
|
+
Views
|
489
|
+
-----
|
466
490
|
|
467
|
-
|
468
|
-
set_accept_cors, :headers=>'Content-Type',
|
469
|
-
end
|
491
|
+
To render views, use the `render_erb` method:
|
470
492
|
|
493
|
+
```ruby
|
494
|
+
get '/users' do
|
495
|
+
@users = User.all
|
496
|
+
render_erb('users/index', layout: 'layouts/main')
|
471
497
|
end
|
472
|
-
|
473
|
-
within an `options` response the following parameters can be passes.
|
474
|
-
|
475
|
-
:origin=>'www.othersite.com' # specify specific allowed origin.
|
476
|
-
:methods => [:get] # allowed methods
|
477
|
-
:max_age => 86400 # maximum age this auth is valid
|
478
|
-
:headers => ['Content-Type','X-OTHER'] # allow additional headers
|
479
|
-
:credentials => true # allow requests with credentials
|
480
|
-
|
481
|
-
## Views
|
498
|
+
```
|
482
499
|
|
483
500
|
the location of your views defaults to `app/views` otherwise set it manually with:
|
484
501
|
|
485
502
|
globally eg:
|
486
|
-
|
503
|
+
```ruby
|
487
504
|
Blix::Rest.set_erb_root ::File.expand_path('../lib/myapp/views', __FILE__)
|
488
|
-
|
505
|
+
```
|
489
506
|
or per controller eg:
|
490
|
-
|
507
|
+
```ruby
|
491
508
|
class MyController < Blix::Rest::Controller
|
492
509
|
|
493
510
|
erb_dir ::File.expand_path('../..', __FILE__)
|
494
511
|
|
495
512
|
end
|
496
|
-
|
513
|
+
```
|
497
514
|
then within a controller render your view with.
|
498
|
-
|
515
|
+
```ruby
|
499
516
|
render_erb( "users/index", :layout=>'layouts/main', :locals=>{:name=>"charles"})
|
500
|
-
|
501
|
-
|
517
|
+
```
|
518
|
+
( locals work from ruby 2.1 )
|
502
519
|
|
503
520
|
|
504
521
|
#### directory structure
|
@@ -513,152 +530,116 @@ then within a controller render your view with.
|
|
513
530
|
main.html.erb
|
514
531
|
|
515
532
|
|
516
|
-
|
517
|
-
|
518
|
-
Blix::Rest.logger = Logger.new('/var/log/myapp.log')
|
519
|
-
|
520
|
-
|
521
|
-
## Testing a Service with cucumber
|
522
|
-
|
523
|
-
|
524
|
-
in features/support/setup.rb
|
525
|
-
|
526
|
-
require 'blix/rest/cucumber'
|
527
|
-
|
528
|
-
and setup your database connections etc
|
529
|
-
|
530
|
-
|
531
|
-
in features/support/hooks.rb
|
532
|
-
|
533
|
-
reset your database
|
534
|
-
|
535
|
-
|
533
|
+
Logging
|
534
|
+
-------
|
536
535
|
|
537
|
-
|
536
|
+
Configure logging:
|
538
537
|
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
| name | level |
|
543
|
-
| anon | guest |
|
544
|
-
| bob | user |
|
545
|
-
| mary | provider |
|
546
|
-
| paul | user |
|
547
|
-
| admin | admin |
|
548
|
-
|
549
|
-
Given user mary posts "/worlds" with {"name":"narnia"} [..or gets/puts/deletes]
|
550
|
-
Then store the "id" as "world_id"
|
551
|
-
|
552
|
-
Given user bob posts "/worlds/:world_id" with {"the_world_id"::world_id }
|
553
|
-
|
554
|
-
Then the status should be 200
|
555
|
-
Then the data type should be "r_type"
|
556
|
-
Then the data length should be 3
|
557
|
-
Then there should be an error
|
558
|
-
Then the error message should include "unique"
|
559
|
-
Then the data "name" should == "bob"
|
560
|
-
Then the data should include "name"
|
561
|
-
|
562
|
-
And explain
|
563
|
-
|
564
|
-
|
565
|
-
NOTE : if you need to set up your database with users then you can use the following hook ..
|
566
|
-
|
567
|
-
in features/support/world.rb .........
|
568
|
-
|
569
|
-
|
570
|
-
require 'blix/rest/cucumber'
|
571
|
-
|
572
|
-
class RestWorld
|
573
|
-
|
574
|
-
# add a hook to create the user in the database -
|
575
|
-
#
|
576
|
-
def before_user_create(user,hash)
|
577
|
-
name = hash["name"]
|
578
|
-
u = MyUser.new
|
579
|
-
u.set(:user_wid, name)
|
580
|
-
u.set(:name,name)
|
581
|
-
u.set(:is_super,true) if hash["level"] == "super"
|
582
|
-
u.save
|
583
|
-
store["myuser_#{name}_id"] = u.id.to_s
|
584
|
-
end
|
585
|
-
end
|
586
|
-
|
587
|
-
now you can also use eg `:myuser_foo_id` within a request path/json.
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
## Manage Assets
|
592
|
-
|
593
|
-
|
594
|
-
require 'blix/assets'
|
595
|
-
|
596
|
-
|
597
|
-
The asset manager stores a hash of the asset data and the current unique file suffix for each asset in its own file.
|
598
|
-
This config file is stored in a config directory. The default is 'config/assets' but another location can be specified.
|
599
|
-
|
600
|
-
|
601
|
-
Blix::AssetManager.config_dir = "myassets/config/location" # defaults to `"config/assets"`
|
602
|
-
|
603
|
-
|
604
|
-
### Compile your assets
|
605
|
-
|
606
|
-
require 'blix/assets'
|
607
|
-
|
608
|
-
......
|
609
|
-
......
|
610
|
-
ASSETS = ['admin.js', 'admin.css', 'standard.js']
|
611
|
-
ASSETS.each do |name|
|
612
|
-
|
613
|
-
compiled_asset = environment[name].to_s
|
614
|
-
|
615
|
-
Blix::AssetManager.if_modified(name,compiled_asset,:rewrite=>true) do |a|
|
616
|
-
|
617
|
-
filename = File.join(ROOT,"public","assets",a.newname)
|
618
|
-
puts "writing #{name} to #{filename}"
|
619
|
-
File.write filename,compiled_asset
|
620
|
-
|
621
|
-
File.unlink File.join(ROOT,"public","assets",a.oldname) if a.oldname
|
622
|
-
end
|
623
|
-
|
624
|
-
end
|
625
|
-
|
626
|
-
|
627
|
-
### In your erb view
|
538
|
+
```ruby
|
539
|
+
Blix::Rest.logger = Logger.new('/var/log/myapp.log')
|
540
|
+
```
|
628
541
|
|
542
|
+
and log a message:
|
629
543
|
|
630
|
-
|
544
|
+
```ruby
|
545
|
+
Blix::Rest.logger.info 'my message'
|
546
|
+
```
|
631
547
|
|
632
|
-
|
548
|
+
Testing with Cucumber
|
549
|
+
---------------------
|
633
550
|
|
634
|
-
|
551
|
+
For testing with Cucumber, install the `blix-rest-cucumber` gem and
|
552
|
+
follow the setup instructions in the README.
|
635
553
|
|
636
|
-
|
554
|
+
Asset Management
|
555
|
+
----------------
|
637
556
|
|
638
|
-
|
557
|
+
For asset management capabilities, you can use the separate
|
558
|
+
`blix-assets` gem. This gem provides tools for managing and
|
559
|
+
serving assets such as JavaScript and CSS.
|
639
560
|
|
640
|
-
|
561
|
+
To use asset management with your Blix::Rest application:
|
641
562
|
|
563
|
+
1\. Install the `blix-assets` gem:
|
642
564
|
|
643
|
-
|
565
|
+
```
|
566
|
+
gem install blix-assets
|
567
|
+
```
|
644
568
|
|
645
|
-
|
569
|
+
2\. Require the gem in your application:
|
646
570
|
|
571
|
+
```ruby
|
647
572
|
require 'blix/assets'
|
573
|
+
```
|
574
|
+
|
575
|
+
3\. Configure the asset manager:
|
576
|
+
|
577
|
+
```ruby
|
578
|
+
Blix::AssetManager.config_dir = "config/assets"
|
579
|
+
```
|
580
|
+
|
581
|
+
4\. Use the asset manager in your controllers:
|
582
|
+
|
583
|
+
```ruby
|
584
|
+
asset_path('assets/main.js')
|
585
|
+
```
|
586
|
+
|
587
|
+
5\. For detailed information on how to use `blix-assets`, please refer to
|
588
|
+
its documentation and README file.
|
589
|
+
|
590
|
+
Controller Helper Methods
|
591
|
+
-------------------------
|
592
|
+
|
593
|
+
Here's a comprehensive list of helper methods available in Blix::Rest controllers:
|
594
|
+
|
595
|
+
- `add_headers`: Add headers to the response
|
596
|
+
- `after`: After hook
|
597
|
+
- `allow_methods`: Allow non-standard HTTP verbs in the controller
|
598
|
+
- `asset_path`: Get the path for an asset
|
599
|
+
- `asset_tag`: Generate an HTML tag for an asset
|
600
|
+
- `auth_error`: Raise an authentication error
|
601
|
+
- `before`: Before hook
|
602
|
+
- `before_route`: Before route hook
|
603
|
+
- `body`: The request body as a string
|
604
|
+
- `body_hash`: The request body parsed as a hash
|
605
|
+
- `env`: The request environment hash
|
606
|
+
- `escape_javascript`: Escape a JavaScript string
|
607
|
+
- `format`: The response format (:json or :html)
|
608
|
+
- `form_hash`: Returns a hash of form data from POST requests
|
609
|
+
- `get_basic_auth`: Get basic authentication credentials
|
610
|
+
- `get_cookie`: Get the value of a cookie
|
611
|
+
- `get_data`: Get a field from the request body's data hash
|
612
|
+
- `get_session_id`: Get or create a session ID
|
613
|
+
- `h`: Escape HTML string to avoid XSS
|
614
|
+
- `logger`: System logger
|
615
|
+
- `method`: The request method (lowercase, e.g., 'get', 'post')
|
616
|
+
- `mode_development?`: Check if in development mode
|
617
|
+
- `mode_production?`: Check if in production mode
|
618
|
+
- `mode_test?`: Check if in test mode
|
619
|
+
- `params`: All parameters combined
|
620
|
+
- `path`: The request path
|
621
|
+
- `path_for`: Give the external path for an internal path
|
622
|
+
- `path_params`: A hash of parameters constructed from variable parts of the path
|
623
|
+
- `post_params`: A hash of parameters passed in the request body
|
624
|
+
- `proxy`: Forward the call to another service
|
625
|
+
- `query_params`: A hash of URL query parameters
|
626
|
+
- `rate_limit`: Apply rate limiting to a block of code
|
627
|
+
- `redirect`: Redirect to another URL
|
628
|
+
- `refresh_session_id`: Generate a new session ID
|
629
|
+
- `render_erb`: Render an ERB template
|
630
|
+
- `req`: The Rack request object
|
631
|
+
- `request_ip`: The IP address of the request
|
632
|
+
- `send_data`: Send raw data with various options
|
633
|
+
- `send_error`: Send an error response
|
634
|
+
- `server_cache`: Get the server cache object
|
635
|
+
- `server_cache_get`: Retrieve/store value in cache
|
636
|
+
- `server_options`: Options passed to the server at creation time
|
637
|
+
- `session`: Access to the session object
|
638
|
+
- `set_accept_cors`: Set CORS headers for the response
|
639
|
+
- `set_status`: Set the HTTP response status
|
640
|
+
- `store_cookie`: Store a cookie in the response
|
641
|
+
- `url_for`: Give the full URL for an internal path
|
642
|
+
- `user`: The user making this request (or nil if not authenticated)
|
643
|
+
- `verb`: The HTTP verb of the request (uppercase, e.g., 'GET', 'POST')
|
648
644
|
|
649
|
-
........
|
650
|
-
|
651
|
-
path = asset_path('assets/standard.js')
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
#### NOTE ON ASSETS!!
|
656
|
-
|
657
|
-
In production mode the compiled version of the assets will be used which will have a unique file name.
|
658
|
-
|
659
|
-
In production the expiry date of your assets can be set to far in the future to take advantage of cacheing.
|
660
|
-
|
661
|
-
In development or test mode the standard name will be used which then will make use of your asset pipeline ( eg sprockets )
|
662
645
|
|
663
|
-
Asset names can contain only one extension. if there are more extensions eg: 'myfile.extra.css' then only the last
|
664
|
-
extension will be used: in this case the name will be simplified to 'myfile.css' !!!
|