Does anyone know how to see supported characters which are handled by Spartacus in section Configurable URLs (especially second Note)?
Note: Some customers have product titles with special characters that will not work (for example, having a slash in the code or title). This might require special treatment of the attributes before or after they are used in the URL. Note that Spartacus does not include functionality for handling special characters.
The router configuration for product routes is driven by static ULR parts and dynamic content form the product model. By default, the product code and name are used in the route configuration, to build out the URL. You can however customise the so-called route parameters. This is a common thing to do when you need a pretty product name in the URL.
Step 1:
normalise the product model, by mapping the product name to a "pretty name". You can do this by implementing a normalizer:
#Injectable({
providedIn: 'root',
})
export class ProductPrettyNameNormalizer
implements Converter<Occ.Product, Product> {
convert(source: Occ.Product, target?: any): Product {
target.prettyName = source.name.replace(/ /g, '-');
return target;
}
}
The normalizer can be provided in Angular, using DI. https://github.com/tobi-or-not-tobi/spartacus-bootcamp/blob/master/src/app/features/routing/product-routes.module.ts shows you an example.
Step 2:
configure the router configuration to use the prettyName as a route parameter. You see an example of this in the fore-mentioned link.
The actual mapping is up to the project. Spartacus does not have a standard mapping as of now.
Related
In my project I have the following requirement: having 2 routes that are dynamic but that are going to be drawn by different components.
More context:
I have a CMS that provides all the info and in most of the cases I'm going to read from a "page" model. This is going to be drawn by the Page component and its going to have a getServerSideProps that makes a call to a CMS endpoint to get that information
The endpoint and component are not the same for the other case with a dynamic route
Both types of routes are completely dynamic so I cannot prepare then in advance (or at least I'm trying to find another solution) since they come from the CMS
As an example, I can have this slugs
mypage.com/about-us (page endpoint & component)
mypage.com/resource (resources endpoint & component)
Both routes are configured using dynamic routes like this
{
source: '/:pages*',
destination: '/cms/pages'
}
The problem here is that it can only match one of the endpoints and the logic for both calling the endpoint and the component used to draw it are completely different
Is there a way of fallbacking to the next matched URL in case that there's more than one that matches similar to the return {notFound: true}?
Some solutions that I have are:
Somehow hardcode all the resources urls (since are less than pages) and pass them as defined routes instead of dynamic routes
Make some dirty code that will check if the slug is from a resource and if not fallback to a page and then return a Resource component or a Page component depending on that (this idea I like it less since it makes the logic of the Page component super dirty)
Thanks so much!
I've been working on backends for a while now and only recently started working a bit on the frontend, which got me nearer to an end-to-end REST implementation.
More to the point, an important principle of REST is to make it discoverable and consistent, so that the client will know how to deal with resources universally (HATEOAS, JsonApi etc).
I've been reading this Google article and there's the following point:
If an API uses HTTP simply and directly, it will only have to document three or four things. (And if an API requires you to read a lot of documentation to learn how to use it, then it is probably not using HTTP as the uniform API.) The four elements of an HTTP API are:
A limited number of fixed, well-known URLs. These are analogous to the names of the tables in a database. For optional extra credit, make all the fixed URLs discoverable from a single one.
and later on....
There is also a shortage of people who understand how to design good HTTP/REST APIs. Unfortunately, we see many examples of APIs that attempt to adopt the entity-oriented HTTP/REST style, but fail to realize all the benefits because they do not follow the model consistently. Some common mistakes are:
Using "local identifiers" rather than URLs to encode references between entities. If an API requires a client to substitute a variable in a URI template to form the URL of a resource, it has already lost an important part of the value of HTTP’s uniform interface. Constructing URLs that encode queries is the only common use for URI templates that is compatible with the idea of HTTP as a uniform interface.
I agree with both, but I fail to see how this can be achieved.
So here's the scenario:
API endpoints:
GET openapi.json / wadl / whatever-discovery-mechanism
/articles/
/articles/$id - only for Option 2 below
... (maybe for each entity operation in case of exhaustive discovery like openapi,
but I'd want to keep it minimal for now)
GET /articles
{
data: [
{
title: "Article 1",
links: {
self: "/articles/1",
...
}
}
]
}
GET /articles/$id
DELETE /articles/$id
...
Frontend URLs:
GET /site/articles - a page showing a list / table of articles
GET /site/articles/1 - a page with a form for editing that article
When navigating to /site/articles, the frontend would know to call /articles API endpoint - which is one of the "limited fixed urls" Google mentions.
Deleting / updating is also done given the links returned with the article entity.
With client-side navigation, the frontend can also "redirect" to /site/articles/1.
The tricky part is when a user navigates directly to /site/articles/1 - how would the page know to call /articles/$id without building the URL itself (or somehow translating it)?
These are the options I see:
building the URL (this is basically "common mistake no.1" mentioned above)
// /site/articles/1
const apiUrl = '/articles/' + location.pathname.split('/')[3]
// /articles/1
building the URL from discovery links (a variation of previous option, still quite as bad IMO)
// /site/articles/1
const apiUrl = api.endpoints.articles + location.pathname.split('/')[3] // or replace or whatever
// /articles/1
encoding the entity "self" link in the frontend URL
// /site/articles/L2FydGljbGVzLzE=
const apiUrl = atob(location.pathname.split('/')[3])
// /articles/1
My concern with this is:
it's kinda ugly
insecure (xsrf / open redirect ...)
it forces the frontend to build URLs anyway (that only it understands)
encoding the entity identifier (which, as I take, is the "self" link) and then looking it up in /articles to then call the returned link
// /site/articles/L2FydGljbGVzLzE=
const entityId = atob(location.pathname.split('/')[3])
const apiUrl = api.get(api.endpoints.articles)
.first(article => article.links.self === entityId)
.links.self
// /articles/1
even uglier than 3. 😅
safe enough
seems pointless at first glance...
If you are concerned that a user navigates directly to a page by typing in a URL, then that IS one of the fixed well-known URLs. It's likely anything that is "bookmark-able" will be in that list.
the key here is the phrase "encode references between entities". This isn't a link between entities, it is an initial entry-point, and as such it is ok to build up the URL from scratch. This is an inflexibility, but as an entry-point you have little choice. The "common mistake" would be embedding that "URL-building" throughout the application as you navigate relationships. I.e. having a list of "commenters" on an article by userid, and building those URLs up by coupling the path to users in the articles page.
We have an old Yii application along with new Symfony one.
The basic idea is simple - I need to check if there is a route matching in Symfony application then it is cool, if not then bootstrap Yii application and try to handle the request with it.
The main idea to not instantiate AppKernel (and do not load autoload.php - since there is two different autoload.php for each project) before I am sure there is route matching.
Can I do it somehow?
We've done this before with legacy applications.
There are two approaches you can take.
Wrap your old application inside a symfony project (recommended).
Unfortunately this will indeed load the symfony front-controller and kernel. No way around that. You need to make sure that symfony can't handle the request and to do that the kernel needs to be booted up.
Use sub-directories and apache virtual hosts to load one application vs the other as needed.
Given option 1,
You can either create your own front controller that loads either symfony or yii by reading routes (from static files if using yml or xml, or annotations which will be more complex) OR EventListener (RequestListener) that listens to the HttpKernelInterface::MASTER_REQUEST and ensures that a route can be returned.
Creating your own front controller is the only way that you can make it not load the symfony kernel, but it will require you to write something that understands the routes in both frameworks (or at least symfony's) and hands off the request appropriately.
Event listener example:
public function onkernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
... Code to continue normally, or bootstrap yii and return a custom response... (Can include and ob_start, or make an http request, etc)
}
public function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest']
];
}
As you see, the kernel needs to be booted to ensure symfony can't serve the route. Unless creating your own front controller (as stated above).
A third approach would be to create a fallback controller, which would load up a specified URL if no route was found within symfony. Although this approach is generally used for legacy projects that lack a framework and use page scripts instead of proper routes, and definitely requires the use/help of output buffering.
The EventListener approach gives you the opportunity to create a proper Request to hand off to yii, and using what is returned to create a Response as proper symfony object (can also use ob or other options).
Thank you.
This is an alternative to vpassapera's solution -http://stovepipe.systems/post/migrating-your-project-to-symfony
I want to detect current selected language from a domain (like es.domain.com, de.domain.com) so I need to pass it to all non static route handlers and to all views.
To detect a language I need a request object. But global view context it is possible to update where request object is not accessible (in server.views({})). Also server.bind (to pass data to route handler) works only where request object is not accessible.
Hapi version: 11.1.2
You could try something like this:
server.ext('onPreResponse', function (request, reply) {
if (request.response.variety === 'view') {
request.response.source.context.lang = request.path;
}
reply.continue();
});
This will attach a lang data point to the context that is being sent into the view. You'll have to extract the lang from the url as request.path is probably not what you actually want.
Also, if you look here you'll see a few pieces of request data is made available to every view via reply.view() If the locale/language is available directly in one of those data points, or can be derived from them, you can skip the extension point approach entirely.
Again, this is assuming version 10+ of hapi. If you're using an older version, the extension point method is your best bet.
I am using Zend F/W 1.12 in order to build a REST server.
One of my requirements are to have an action that Is outside the boundaries of what Zend can recognize as a "Restfull" action. What I mean is that I would like to have an action that is called something like mymedia and would like tou routes requests that are directed to //mymedia . Currently, Zend understand it as the id to a getAction and off course this is not what I want.
Any help will be highly appreciated!
Thanks
The implementation of Zend_Rest_Route does not allow much customization but instead provides a rudimental routing scheme for out-of-the-box usage.
So if you need to change the way how URIs are interpreted you can extend Zend_Rest_Route, Zend_Controller_Router_Route_Module or Zend_Controller_Router_Route_Abstract class to create your own kind of routing.
Have a look at the match method of those classes and what they do - e.g. they populate the $_values property array (while respecting the $_moduleKey, $_controllerKey and $_actionKey properties).
You can then add it e.g. as the first route within your bootstrap class:
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$router->addRoute('myRoute', new My_Route($frontController));
$router->addRoute('restRoute', new Zend_Rest_Route($frontController));
See:
http://framework.zend.com/manual/1.12/en/zend.controller.router.html#zend.controller.router.basic
Routing is a simple process of iterating through all provided routes and matching its definitions to current request URI. When a positive match is found, variable values are returned from the Route instance and are injected into the Zend_Controller_Request object for later use in the dispatcher as well as in user created controllers. On a negative match result, the next route in the chain is checked.
I once wrote a custom route for zend framework 1 that can handle custom restful routes. it served me well until now. see https://github.com/aporat/Application_Rest_Controller_Route for more details.
for example, if you want to have a url such as /users/30/messages mapped correctly into a zend controller action, use this route in your bootstrap:
$frontController = Zend_Controller_Front::getInstance();
$frontController->getRouter()->addRoute('users-messages', new Application_Rest_Controller_Route($frontController, 'users/:user_id/messages', ['controller' => 'users-messages']));