-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Docs: How to receive context in controller #1881
Comments
@diegolacarta in a controller method you can access |
Ideally, you should decouple the code building request data from your controllers to enable you to test the controllers in isolation (unit-test style). Typically, request headers are mapped to regular method parameters, OpenAPI spec supports I think it makes sense to expose the client IP via a custom binding that will allow you to receive the IP address in your controller methods via dependency injection (e.g.
Since you are interested in the Request object only, you can configure your controller to receive the request as follows: class MyController {
constructor(
@inject(RestBindings.Http.REQUEST) public request: Request,
// ...
) {}
@get('/foo')
foo() {
console.log(this.request.headers);
}
} Accessing the context directly should be the last resort solution. You can receive the current context using class MyController {
constructor(
@inject.context() public context: RequestContext,
// ...
) {}
@get('/foo')
foo() {
console.log(this.context.request.headers);
}
} |
@diegolacarta since you closed the issue, I assume my answer provided a solution for the problem you are facing. It makes me wonder how can we improve our documentation so that future LB4 users can more easily discover the information I have provided in my comment. Do you have any suggestions? I am assuming you have read our docs at https://loopback.io/doc/en/lb4/index.html but didn't find the answer. Where were you looking (which pages did you read)? Where would it makes most sense to document how to access the request from a controller method? |
Yes, your answered was the perfect solution.
I'm struggling a bit with the documentation for v4, particularly since I'm
migrating a relatively complex express app into it. Things like accessing
headers, setting/clearing cookies, uploads, authentication, is not easy to
find (and I believe some of it doesn't exist yet).
I would expect to find your answer in here:
https://loopback.io/doc/en/lb4/Controllers.html but there's no mention to
how to access request/response from there.
I've gone through all pages in https://loopback.io/doc/en/lb4/index.html,
maybe I missed it.
Diego
…On Mon, 22 Oct 2018 at 10:18 Miroslav Bajtoš ***@***.***> wrote:
@diegolacarta <https://github.com/diegolacarta> since you closed the
issue, I assume my answer provided a solution for the problem you are
facing. It makes me wonder how can we improve our documentation so that
future LB4 users can more easily discover the information I have provided
in my comment. Do you have any suggestions? I am assuming you have read our
docs at https://loopback.io/doc/en/lb4/index.html but didn't find the
answer. Where were you looking (which pages did you read)? Where would it
makes most sense to document how to access the request from a controller
method?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1881 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AGTtN2PznRJn21fEQDNl-REwFt8YIX_rks5unX9YgaJpZM4Xv7o5>
.
|
I am reopening this issue. Let's improve our docs!
Makes sense. Would a migration guide from Express to LB4 be helpful to you? Could you please help me to define specific use cases to describe in our docs (e.g.
We haven't considered cookies yet. We have #1863 requesting support for sessions. What are looking for - HTTP sessions or generic support for cookies?
We have discussing file uploads here: #1863
Authentication and authorization is in our backlog now. Could you please be more specific about your auth/auth requirements? Do you store user credentials locally (e.g. email + password) or rely on OAuth2, OpenID, SAML or another technology? What authorization style do you use - role-based, access-control lists, scopes, something else? If you look back to the time you were reading our docs, would you find it helpful if the Controller docs was clearly spelling out which HTTP features are not implemented yet (cookies, file uploads), with pointers to the relevant GitHub issues? |
I got here searching for a way to get the user IP. I hope I find "RestBindings" helpful in many things I could need in the future. By the way, the following: @inject(RestBindings.Http.REQUEST) public request: Request, Returned the error: Note: this error was not caught by TypeScript. It was returned only after Had to go with: @inject(RestBindings.Http.REQUEST) public request: any, I am using PassportJS Basic Strategy based on the lb4 authentication example. Another note is that I tried to get the request or the context inside an authentication provider file, but the request body was always not present in the request. Everything else was there (headers, connection, socket, ServerResponse,..) but not the body. For that reason, I moved my logic to a controller rather than the provider. I don't want the method to be called first. I just want to include the body the same way the header is included in the req. Like the req is sent with the header, so why not the body too? I have a question:
@bajtos What do you mean? If I am checking for the existence of something in the request body for example, I should not do this in the controller? |
@diegolacarta sorry for a late response, I have too many GitHub notifications.
Here is the place where we are creating request-specific bindings:
I suspect this is a bug in TypeScript typings. One of the libraries we use in LoopBack is incorrectly assuming browser environment. To make TypeScript happy, we need to include
I am not entirely sure what is the cause of the behavior you are seeing. I think this may be caused by the fact that Can you try to use
Your controller should describe the request body via OpenAPI spec and then receive the parsed (and validated!) body object as a parameter. See our Todo example: That place does not provide any schema in @post('/todos', {
responses: {
'200': {
description: 'Todo model instance',
content: {'application/json': {schema: {'x-ts-type': Todo}}},
},
},
})
async createTodo(@requestBody() todo: Todo) {
return await this.todoRepo.create(todo);
} The decorator const UserSchema = {
// OpeAPI/JSON schema describing the payload
};
class MyController {
@put('/Users/{id}')
async replaceUser(
@param.path.string('id') id: string,
@requestBody({
content: {
'application/json': UserSchema,
},
})
user: object,
) {
// the implementation
}
} Further reading: |
@diegolacarta Please make sure you have the following statement: import {Request} from '@loopback/rest' In VSCode, |
This problem is caused by a problem in type definitions of If you are not using |
I found a neat solution how to fix this problem, see #2621 |
How to receive context in unit testing controllers? import {AuthController} from '../../controllers/auth.controller';
import {UserRepository} from '../../repositories';
import {testdb} from '../../datasources/test.datasource';
export async function authController(): Promise<AuthController> {
const userRepository = new UserRepository(testdb);
return new AuthController(
userRepository,
// here should be context to access headers, etc
);
} |
You can pass in context via the constructor for unit testing. The other option is to bind the controller to context and retrieve an instance via const ctx = new Context();
ctx.bind('controllers.MyController').toClass(MyController);
const controller = await ctx.get('controllers.MyController'); |
@auxcoder As I explained in #1881 (comment), you shouldn't be injecting the entire context, that's an anti-pattern making unit tests difficult to write. If your controller needs to access request headers, then you can inject the current request object only. Or even better, define bindings for the individual headers you are interested in. |
Reopening the issue to make sure docs are improved. |
Is there any way to receive the context inside a controller?
Goal is to access request data (headers, ip, etc.)
Thanks
Acceptance criteria
@inject(RestBindings.Http.REQUEST) public request: Request
for different cases:The text was updated successfully, but these errors were encountered: