My simplistic understanding of the Rails stack is that it does the following (in general)
The input is a HTTP message (GET, POST, PUT, DELETE, HEAD), a URL, some (optional) cookies, a (optional) session token, and possibly some data packed up in JSON or XML format.
This HTTP message is handled by Rack which may run it through a set of "middleware" functions which add or remove data from the message, and may use the data to add, alter, or remove data from the program's data store.
The HTTP message at the output of Rack is sent to the Rails router, which maps it to a controller, an action, and a params hash with the session information, cookies, and other parameters in it. The data is dispatched to the appropriate controller.
The controller method parses the params, and algorithmically combines it with data from the database (via models), optionally changes data and then dispatches a set of variables starting with # to a view template.
The view template takes the data and merges it with the view template to form a view with the appropriate format (eg. HTML, JS, JSON, XML, etc), or it tells the caller (via a HTTP return code) to redirect to another URL.
What I would like to do with my Rails web application, using the Rails console is
Compose a valid HTTP message with URL, session ids, cookies, and encoded data (JSON or XML).
Inject this data into the Rack interface, and inspect what comes out before it is sent to the Rails router.
I would then like to then send that data to the Rails router and see what comes out before it goes to the controller.
I would then like to send that data to the controller and see what comes out before it is sent to the view template.
And I would then like to take that that data and run it through the view template and have display on STDOUT, or have it automatically open a web browser and see it render.
Are there any existing Rails tools which can do this? If not, can anybody point me to the Rails code where these interfaces occur?
Most of what your looking for is within the actionpack library of rails.
ActionDispatch handles the interface with rack, the middleware and the router. The request hits the router as the last piece of middleware. The router itself gets called within action_dispatch/routing/mapper. So if you want to intercept the request before it hits the router, inject your own piece of middleware before the routes get called.
Once the the router map verifies that it has a valid route it calls the application which is over in the railties library. This I believe then bumps you back over into actionpack into the ActionController middleware. I'm not sure exactly where you'd want to tie in to intercept before getting to the controller, but I'm guessing it would be somewhere around that ActionMiddleware class.
From there your off into actionpack. The render/redirect that your controller calls sets up what will happen within actionpack. I'm not sure exactly where AC calls into AP but you should be able to sift your way through it and find the path your looking for.
As for the response, your back to the middleware. Once rails has the view stitched together it basically unwinds itself and goes back down the middleware stack before sending the response back to Rack.
Hope that helps.
Related
VueJS + Quasar + Pinia + Axios
Single page application
I have an entity called user with 4 endpoints associated:
GET /users
POST /user
PUT /user/{id}
DELETE /user/{id}
When I load my page I call the GET and I save the response slice of users inside a store (userStore)
Post and Put returns the created/updated user in the body of the response
Is it a good practice to manually update the slice of users in the store after calling one of these endpoints, or is better to call the GET immediatly after ?
If you own the API or can be sure about the behavior of what PUT/POST methods return, you can use local state manipulation. Those endpoints should return the same value as what the GET endpoint returns inside. Otherwise, you might end up with incomplete or wrong data on the local state.
By mutating the state locally without making an extra GET request, the user can immediately see the change in the browser. It will also be kinder to your server and the user's data usage.
However, if creating the resource(user, in this case) was a really common operation accessible by lots of users, then calling the GET endpoint to return a slice would be better since it would have more chance to include the new ones that are created by other users. But, in that case, listening to real-time events(i.e. using WebSockets) would be even better to ensure everyone gets accurate and new data in real-time.
I have a Vue / Nuxtjs app which displays lots of user-provided content (think of it as a crowdsourced blog). The content on the client is retrieved and stored in Vuex. When a page is loaded, it displays the current content and then uses fetch to get the updated data. Here is a typical component:
fetch() {
this.$store.dispatch('feeds/refreshLatest')
},
computed: {
feed() {
return this.$store.state.feeds.latest
}
}
where feeds/refreshLatest uses axios to retrieve the posts.
This works quite well. The problem is the initial load is very slow, especially on the front page which has to process and display dozens of articles.
I have SSR enabled, and would like the server to store the content, and then on initial load provide a rendered page to the client. However, the Vuex object on the server seems to be new for each request, and so the client has to wait for the entire set of articles to be fetched before anything is displayed, which is unacceptable. Doing all the fetches only on the client solves this problem, but it is still too slow.
I thought I could somehow use the same server Vuex on each call and sending it to the client with nuxtServerInit, but I don't see a way to achieve sharing the Vuex. Thank you for any pointers or other packages which could help.
The question is that after the fetch is finished after the api call in the server rendering, the DOM is dropped to the client, and the process is running every time and slow?
I solved similar issues using cookies. This is because cookies can also be used to render servers. I used the method below.
Store the data in the cookie after the initial api call, and send the data in the cookie to the client first.(If cookies are present, do not call api from server)
Call api from client to update data.
I use this library.
https://github.com/microcipcip/cookie-universal/tree/master/packages/cookie-universal-nuxt#readme
Suppose that I invoke the following HTTP request:
https://accounts.example.com/oauth2/auth?
scope=openid+email&
nonce=53f2495d7b435ac571&
redirect_uri=https%3A%2F%2Foauth2demo.appspot.com%2Foauthcallback&
response_type=id_token+token&
client_id=753560681145-2ik2j3snsvbs80ijdi8.apps.googleusercontent.com
Which yields the following redirect response:
https://oauth2demo.appspot.com/oauthcallback#
access_token=ya29.AHES6ZSzX
token_type=Bearer&
expires_in=3600&
id_token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY...
What is the point of the callback parameter, given that the returned metadata, containing id_token, etc., is positioned after a hash fragment in the URI, and are therefore not persisted as parameters to the callback function? How can a server-side callback receive the various tokens?
The response type that is used here is a so-called "Implicit" response type which is primarily meant for in-browser (e.g. Javascript) clients, in which case the Javascript code that is served on the callback URL can access the parameters in the fragment. Web applications should do either one of:
stick to the code flow which is meant for web application clients
use the Form Post response mode (http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html) if supported by the OP
serve Javascript on the callback URL that parses out the parameters from the fragment and POSTs them to the server
Bottom line is that if you need the tokens server-side, you should most probably use the code response type.
I am having a very similar issue to this post here: How to use custom route middleware with Sails.js? (ExpressJS)
in that I want all non ajax requests (or all routes with the prefix /api) to load the same view, regardless of route. I have implemented the given answer in that question, but came across the issue that the policy is not called for any unspecified routes.
If I was to catch all routes so that the policy was called, all my blueprints would be overwritten.
Ideally, I would catch all routes last, after the blueprints, since every non API route should be sent to the front end.
I am using angularjs for the front end and want angular to deal with all non API routing.
I would rather not use a .htaccess file as I need to put session information into the page on it's initial load.
Thanks
It seems like your use case is very similar to a HTTP 404 Error situation - you want all requests which don't satisfy blueprint (and possibly route.js) routes to be handled in the same manner.
From api/responses/notFound.js:
* NOTE:
* If a request doesn't match any explicit routes (i.e. `config/routes.js`)
* or route blueprints (i.e. "shadow routes", Sails will call `res.notFound()`
* automatically.
*/
You can have special handling code here which calls the appropriate view if the request path contains /api:
if (req.path.match('^/api')) {
return res.view('your-view-here');
}
Instapaper, if you don't know it, is a bookmarklet that saves your current URL to an account of yours. Essentially the bookmarklet loads a script on the page with parameters on that script's URL with something like
z.setAttribute('src', l.protocol '//www.instapaper.com/j/Jabcdefg?u='
encodeURIComponent(l.href)'&t=' (new Date().getTime()));
b.appendChild(z);
So that's sending a request to a user-based, obfuscated URL along with the current page's URL.
I'm wondering how a similar service would be set up in a Rails app. The work is clearly being done by something called, perhaps, parser, which would probably be a model (it will run an HTTP request, parse, and save the data, for example). Can you route directly into a model? Do you need a controller over it to handle incoming requests? (I've tried this last bit, and it auto-loads a view, which I don't need/want).
I'd love some advice on this general architecture. Thanks!
I guess you cannot route directly to a model.
So, you need a controller over it to handle incoming requests.
And use "render :nothing => true" if you don't want the view to be sent to the browser.