Open blablabla1234678 opened 2 months ago
I intend to support express and Laravel atm.
Laravel might take a bundle or middleware or just calling the code from the routes/api.php
file.
Express is a lot easier since it is js.
First I'll finaliize the type conversion, validation and other stuff with the current mock solution and maybe with express. Second I'll write the PHP.
Express code looks like this:
app.get('/user/:id', function(req, res) {
res.send('user' + req.params.id);
});
As far as I can tell there is no query option. The query can be used with req.query.x
.
Since query is part of the URI which is the resource identifier, it must be used for routing. I'll check maybe we have a REST module for express which includes the query as well. If not, then we must write a routing middleware I guess. Laravel has the same routing behavior.
I asked on SO, maybe somebody knows the reason why query is omitted in routing in many popular frameworks, though .NET supports it. Okay, let's redesign routing a little bit. Currently I have routes like this: /a/{a}?{b}&{c}
. This is not valid URI template. I want to support URI templates, though not the complete standard, just basics.
All query parameters mandatory: /a/{a}?b={b}&c={c}
. Query parameters optional: /a/{a}?{?b,c}
.
If we check the URI template RFC: https://datatracker.ietf.org/doc/html/rfc6570 They are talking about level 1-4 rules. Level 1 is mandatory variables. /a/{a}?b={b}&c={c}
Level 2 can be optional I think. /a{+a}
where a="/123"
. Level 3 adds multiple variables in a single statement: ?{x,y}
for ?123,456
. It is not explicitly written in the RFC, but I have the impression that {/x}
is an optional parameter while /{x}
is a mandatory parameter. Let's stick to this and say that /a/{a}
matches "/a/123"
while {/a}
matches both "/a/123"
and ""
. Though I still want to investigate if I am right.
First things first we need to decide whether we want to support multiple templates for the same resource which I think we want to do. Especially query strings are interesting because the query part can be in many different oders e.g. {?a,b,c}
, {?c,b,a}
, etc... I think the order should not matter here... Multi template support is necessary if we want to support multiple languages in URI templates, but I guess we don't need that because we are talking about M2M communication here, so English is perfect for it. If so, then multi URI template support is not necessary at all if we can express optional variables in the URI templates. If not, then we can do it only with multiple templates. Another issue here that we will describe in the hyperlink's parameter description that required:true
and if it is not present, then the parameter won't be required. So in theory all parameters should be optional in the template and the hyperlink description should decide whether they are required for the actual call. This makes things hard, but I can give it a try.
The /a/{a}?b={b}&c={c}
should be {/a}{?b,c}
or /a{/a}{?b,c}
, hard to decide. What I really want to do is something like /people/friends?search=John
. /people/{filter1}?search={filter2}
or /people{/filter}{?search}
and {filter: {required:false}, search: {required:true}}
. I don't like it. Another option is /people/{filter1}?search={filter2}
and /people?search={filter2}
. This way we can support both, but if we have many optional parameters we have to write a very long list of combinations of optional parameters, so I would not do this either. Yet another option is not describing URI parameters in request parameters which is possible, but not a good idea since we describe type there too for type conversion.
listPeople: {
type: "Hyperlink",
uri: "/people?{page}",
method: "get",
request: {
type: "FlatObject",
items: {
page: {
type: "Number"
}
}
},
response: {
type: "Array",
items: {
type: "Person"
}
}
},
{
type: "listPeople",
request: {
page:123
}
}
Here the page should be optional and converted to Number. The request looks like this: GET /people?page=123 {}
All the controller should get is controller.listPeople({page:123})
and all of this must happen automatically.
We can have multiple controllers and routing depends only on their supported methods name. E.g. if listPeople
is on the controller, then it is called in the upper case.
service.register({listPeople: function (parameters){
let page = parameters.page?parameters.page:1;
let itemsPerPage = 25;
let lastPage = Math.ceil(this.userRepository.count()/itemsPerPage);
if (page > lastPage || page < 1)
throw new Error('Out of range.');
let nextPage = page == lastPage?lastPage:(page + 1);
let previousPage = page == 1?1:(page - 1);
let people = this.userRepository.listPage(page, itemsPerPage);
return {
items: people,
hyperlinks: {
previous: {type: "listPeople", request: {page: previousPage}},
self: {type: "listPeople", request: {page: page}},
next: {type: "listPeople", request: {page: nextPage}}
}
};
}});
The next feature to implement REST service generation. Without validation for now.