This question already has answers here:
Gin router: path segment conflicts with existing wildcard
(2 answers)
Closed 10 months ago.
I'd like to build a gin program which serves the following routes:
r.GET("/special", ... // Serves a special resource.
r.Any("/*", ... // Serves a default resource.
However, such a program panics at runtime:
[GIN-debug] GET /special --> main.main.func1 (2 handlers)
[GIN-debug] GET /* --> main.main.func2 (2 handlers)
panic: wildcard route '*' conflicts with existing children in path '/*'
Is it possible to create a gin program which serves a default resource for every route except for a single one which serves a different resource?
Many pages on the web lead me to believe it is not possible using the default gin router, so what is the easiest way to serve these routes from a gin program?
You can try like this.
route.GET("/special/*action", func(ctxt *gin.Context) {
ctxt.JSON(200, gin.H{"message": "WildcardUrl"})
})
Looks like the gin.NoRoute(...) function will do the trick.
r.GET("/special", func(c *gin.Context) { // Serve the special resource...
r.NoRoute(func(c *gin.Context) { // Serve the default resource...
See also https://stackoverflow.com/a/32444263/244128
Maybe, someone else (like me) will face that error message and will have a situation where gin.NoRoute() is not an acceptable fix.
In search for a workaround to this issue I found the following snippet from github:
...
router.GET("/v1/images/:path1", GetHandler) // /v1/images/detail
router.GET("/v1/images/:path1/:path2", GetHandler) // /v1/images/<id>/history
...
func GetHandler(c *gin.Context) {
path1 := c.Param("path1")
path2 := c.Param("path2")
if path1 == "detail" && path2 == "" {
Detail(c)
} else if path1 != "" && path2 == "history" {
imageId := path1
History(c, imageId)
} else {
HandleHttpError(c, NewHttpError(404, "Page not found"))
}
}
Related
I'd like to use pion 5.0.6 as a small webserver in a VS2017 c++ project. For static routes I can use
add_resource("/my/static/route", <handler>)
I would need dynamic routes as well - like "/data/:id/info
How do I do this?
For those who may need it: I found a solution to add dynamic routing to the pion webserver. It requires smart router code I found at hxoht on github, and works the way that
all routes - static and dynamic - are set with httpd->add_resource(<url>, <handler);
a 404-handler has to be set with httpd->set_not_found_handler(<handler>); and is responsible for dispatching the dynamic routes to the handlers added above.
your webserver class must derive from pion::http::server in order to find the handler by name with httpd->find_request_handler(<url>, <handler>);
in your 404-handler, you use the Match::test(<dynamic-route>) method to detect a dynamic route - like in the following code fragment:
void handle_404(http::request_ptr& req, tcp::connection_ptr& con)
{
Route target;
Match dynamic = target.set(req->get_resource());
for (auto& route : dynamic_routes) // Our list of dynamic routes
{
if (dynamic.test(route)) // Does the url match the dynamic route pattern?
{
request_handler_t h;
if (find_request_handler(route, h))
{
auto name = get_param_name(route); // e.g. /a/:b -> "b"
value = dynamic.get(name); // Save value in string or map<name, value>
h(req, con); // Call original handler with value set properly
return;
}
}
}
// If no match then return a 404.
http::response_writer_ptr w(http::response_writer::create(con, *req,
boost::bind(&tcp::connection::finish, con)));
http::response& res = w->get_response();
res.set_status_code(http::types::RESPONSE_CODE_NOT_FOUND);
res.set_status_message(http::types::RESPONSE_MESSAGE_NOT_FOUND);
w->send();
}
For using the pion webserver in a multi-threaded way, I would store the parsed value inside the request object, which would be derived from pion::http::request.
This would work for Windows and Linux :)
app.get('/:service[SOMETHING GOES HERE]', function(req, res, next){
console.log('Service is:', req.params.service);
});
This needs to catch URLs that can look like any one of:
/foo
/foo/bar
/foo/bar/baz
The call back isn't concerned with anything that comes after foo, but ideally should be able to access foo as a parameter called service without having to manually parse the path.
I've been using this to test and still haven't found anything that does exactly that. Closest so far is /:service*.
Edit: No it's not a duplicate of the one where the answer is /:service/* because that doesn't cover /foo.
Using /:service* in actual Express routes does exactly what you want:
/foo maps to { '0': '', service: 'foo' }
/foo/bar maps to { '0': '/bar', service: 'foo' }
/foo/bar/blah maps to { '0': '/bar/blah', service: 'foo' }
The Express Route Tester, for some reason, maps these URL's differently for that these kinds of patterns (it might be configured differently than Express).
You can use the app.use() function for that. Read the doc about path handling for more info. Your code once modified will be:
app.use('/foo', function(req, res, next){
console.log('Service is:', req.params.service);
});
The downside is that you are not going to recover foo as the service parameter.
I want to be able to retrieve a certain conversation when its id is entered in the URL. If the conversation does not exist, I want to display an alert message with a record not found.
here is my model hook :
model: function(params){
return this.store.filter('conversation', { status : params.status}, function(rec){
if(params.status == 'all'){
return ((rec.get('status') === 'opened' || rec.get('status') === 'closed'));
}
else{
return (rec.get('status') === params.status); <--- Problem is here
}
});
}
For example, if I want to access a certain conversation directly, I could do :
dev.rails.local:3000/conversations/email.l#email.com#/convid
The problem is when I enter a conversation id which doesn't exist (like asdfasdf), ember makes call to an inexisting backend route.
It makes a call to GET conversation/asdfasdf. I'm about sure that it is only due to the record not existing. I have nested resources in my router so I'm also about sure that it tries to retrieve the conversation with a non existing id.
Basically, I want to verify the existence of the conversation before returning something from my hook. Keep in mind that my model hook is pretty much set and won't change, except for adding a validation on the existence of the conversation with the id in the url. The reason behind this is that the project is almost complete and everything is based on this hook.
Here is my router (some people are going to tell me you can't use nested resources, but I'm doing it and it is gonna stay like that so I have to work with it because I'm working on a project and I have to integrate ember in this section only and I have to use this setup) :
App.Router.map(function(){
// Routing list to raw namespace path
this.resource('conversations', { path : '/' }, function() {
this.resource('conversation', { path : '/:conversation_id'});
});
});
This also happens when I dont specify any id and I use the hashtag in my url like this :
dev.rails.local:3000/conversations/email.l#email.com#/ would make a call to conversation/
I know it is because of my nested resource. How can I do it?
By passing a query to filter (your { status : params.status}) you are asking Ember Data to do a server query. Try removing it.
From the docs at http://emberjs.com/api/data/classes/DS.Store.html#method_filter:
Optionally you can pass a query, which is the equivalent of calling find with that same query, to fetch additional records from the server. The results returned by the server could then appear in the filter if they match the filter function.
So, remove the query:
model: function(params){
return this.store.filter('conversation', function(rec) {
if (params.status == 'all') {
return rec.get('status') === 'opened' || rec.get('status') === 'closed';
} else {
return rec.get('status') === params.status;
}
});
}
Ok so here is what I did. I removed my nested resource because I realised I wasn't using it for any good reason other than redirecting my url. I decided to manually redirect my url using javascript window.location.
This removed the unwanted call (which was caused by the nested resource).
Thanks to torazaburo, you opened my eyes on many things.
My team decided that we wanted to be consistent with our filename naming conventions and decided that we want to stick with underscores across all of our projects; this is not just Ember/frontend projects, but APIs, DBs, pattern library, style guides, etc. We also want to use the Ember CLI for or next frontend project. Is there an option to change the "dasherized" naming convention to be "underscored?" If not, how would we go about solving this? I'm hoping the solution would be easier than just making our own fork of the CLI.
Based on runspired's suggestion, here's a custom resolver that converts modules with _ to -:
import Resolver from 'ember-resolver';
import ModuleRegistry from 'ember-resolver/utils/module-registry';
function DasherizedModuleRegistry(entries) {
var keys, key;
this._entries = entries || requirejs.entries;
// Convert underscore in module names to dashes
keys = Object.keys(this._entries);
keys.forEach((key) => {
var dasherizedKey = key.replace(/_/g, '-');
if (dasherizedKey !== key) {
this._entries[dasherizedKey] = this._entries[key];
delete this._entries[key];
}
});
}
DasherizedModuleRegistry.prototype = Object.create(ModuleRegistry.prototype);
DasherizedModuleRegistry.prototype.constructor = DasherizedModuleRegistry;
export default Resolver.extend({
init: function() {
this._super();
if (!this._moduleRegistry || !(this._moduleRegistry instanceof DasherizedModuleRegistry)) {
this._moduleRegistry = new DasherizedModuleRegistry();
}
},
});
Place this into app/resolver.js in your ember-cli app. We have a mix of components/foo_bar.js and components/bar-foo.js and this resolver works for us.
Note that this conversion will be run in the client's browser on application boot and might slow it down with a large codebase.
I want to be able to inject services into my controllers, so I had a look at http://symfony.com/doc/current/cookbook/controller/service.html and after some fiddling with the notation (could be a little more consistent but whatever) I have my WebTestCase using the service definition entry.
But the controller needs the container itself injected (and does indeed extend ContainerAware via the default framework controller), and the ControllerResolver in the FrameworkBundle does not do that.
Looking at the code (Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver::createController()) this is not suprising:
protected function createController($controller)
{
if (false === strpos($controller, '::')) {
$count = substr_count($controller, ':');
if (2 == $count) {
// controller in the a:b:c notation then
$controller = $this->parser->parse($controller);
} elseif (1 == $count) {
// controller in the service:method notation
list($service, $method) = explode(':', $controller, 2);
return array($this->container->get($service), $method);
} else {
throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller));
}
}
list($class, $method) = explode('::', $controller, 2);
if (!class_exists($class)) {
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
}
$controller = new $class();
if ($controller instanceof ContainerAwareInterface) {
$controller->setContainer($this->container);
}
return array($controller, $method);
}
Apparently when using service:method notation,, it directly returns the controller from the container, not injecting the container itself.
Is this is a bug or am I missing something?
This is not a bug. It works as expected. This workflow generally "protects" the Controller as a Service concept. This way You need to look at Controller as a regular Service. In regular Service You inject everything what You need - if You need the controller itself - inject it explicitly.
To explain it more clearly, this "protection" I mentioned helps to avoid using service:method notation in one place and controller::method or bundle:controller:method in another.
So if not this "protection" it would be difficult to state that particular Controller is described as a Service or not as this would depend on which notation will be called first in Container build-up.