In my application I mount following URL:
this.mountPage("/details/${site}", MerchantDetailPage.class);
So a request to for instance ../details/anything will create an instance of MerchantDetailPage with pageparameter: site=anything.
The constructor of MerchantDetailPage:
public MerchantDetail(final PageParameters parameters) {
super();
org.apache.wicket.util.string.StringValue storeParameter = parameters.get("site");
if (!storeParameter.isEmpty()) {
this.store = this.service.getStoreByQBonSiteWithCategoriesDescriptionsRegionAndAddress(storeParameter.toString());
}
if (store == null) {
throw new RestartResponseAtInterceptPageException(Application.get().getHomePage());
}
// Build the page
this.createPage(this.store, null);
}
This seemed to work fine until I noticed that the constructor was called 4 times.
After some digging I found that the constructor was called once with parameter site=anything but then another 3 times for 3 images that are on the page; e.g.:
<img wicket:id="store_no_image" src="./images/shop_no_logo_big.png" alt="logo" />
So, for this resource Wicket is also calling this page but with parameter: site=images.
As a consequence, the store is null so the request for the image is redirected to the homepage => the image is not found.
Why is this happening? Why is wicket trying to treat a resource request through a page mount?
Some side comments:
MerchantDetailPage has also another constructor which is called directly from the code and accepts the store id as a parameter. In this case the problem does not occur.
if I use an absolute URL for the image it does work (does not enter into MerchantDetailPage for the image request)
Well... your page resides at
/detail/anything
which is correctly mapped to your merchant detail page...
Your images reside at
/detail/images/shop_no_logo_big.png
and similar, which is correctly mapped to your merchant detail page...
The mount path doesn't know and doesn't care if it's a page request or a resource request. For all it is worth it could be the case that you're using the mount path to create the resource dynamically...
So the solution is to move your images to a location that doesn't match yout mount-path.
Related
I have a generic common NodeJS app that multiple users access. The users are identified via the path. For example: https://someapp.web.app/abc can be one path while https://someapp.web.app/def can be another path.
On the NodeJS server path, I send the same server code by passing the path parameters to the program. The route appears something like this:
app.get('/*', async (req, res) => {
...
locals.path = req.path;
...
res.render('index', locals);
}
In the above index is a template that uses locals data for customisation
What I would like is that for each path there is a separate manifest and its associated icons and that on a single device (phone or desktop) multiple installations be possible. Thus, https://someapp.web.app/abc be one icon and https://someapp.web.app/def be another icon.
I am having difficulty in the placement and the scoping of the manifest and service worker. It always adds only one icon (the first path installed) to the home screen or desktop. My settings are:
In the public (root) folder I have each manifest viz. abc-manifest.json and def-manifest.json and a common sw.js.
The abc-manifest.json is:
'scope': '/abc',
'start_url': '/abc',
...
The access to the service-worker from the index.js is:
if (navigator.serviceWorker) {
navigator.serviceWorker.register('sw.js')
.then(function (registration) {
console.log('ServiceWorker registration succeeded');
}).catch(function (error) {
console.log('ServiceWorker registration failed:', error);
});
}
I have tried changing the paths of scope and start_url to / but it did not work. Since all requests to the public path are common and not within the virtual /abc path, I am unable to figure out how to get this working.
Thanks
Could that be an option to have a dedicated route that will redirect the user to /abc or /def?
In the manifest:
{
"start_url": "https://example.com/login",
"scope": "https://example.com/",
}
/login would make sure to redirect to /abc or /def.
This way you could keep one service worker, and one manifest.
And in the Service Worker, maybe try to return the specific icon based on file name.
self.addEventListener('fetch', e => {
// Serve correct icon
let url = new URL(e.request.url)
if (url.pathname.contains('/android-icon-512.png')) {
return respondWith(e, '/android-icon-512-abc.png')
}
// other ifs…
// Return from cache or fallback to network.
respondWith(e, e.request)
})
const respondWith = (e, url) =>
e.respondWith(caches.match(url)
.then(response => response || fetch(e.request).then(response => response))
)
Maybe you’ll need a specific header to do this, or use a URL parameter (icon.png?user=abc) to help query the right icon. I’m throwing idea, because it probably depends a lot on your app back-end and/or front-end architecture.
I once did this: the back-end (PHP / Laravel) handled the correct returning of the icon and manifest (I had one for each use case) based on other stuff.
I’m trying to figure out if it’s possible to customize the name of the node AEM creates when I first drop a component on the page.
The cq:Component node where my component is defined is named “knowledge-center-question” and when I drop it, AEM creates a node named “knowledge_center_que” in the page’s node tree using its default naming logic. I would prefer for the node name to be “question” when it is dropped (but I’d rather not rename the component itself).
It seems like this kind of thing must be possible given how customizable everything is in AEM, but I’m struggling to find an answer.
Take a look at :nameHints, which can be send as POST arguments to the SlingPostServlet: https://sling.apache.org/documentation/bundles/manipulating-content-the-slingpostservlet-servlets-post.html#algorithm-for-node-name-creation
You need to write a custom Sling post processor. Sling post processor is called after a component dropped in the page. Example code :
#Component(service = SlingPostProcessor.class, immediate = true, name = "com.aem.CustomPostProcessor")
public class CustomPostProcessor implements SlingPostProcessor {
#Override
public void process(SlingHttpServletRequest request, List<Modification> modifications) throws Exception {
if (accepts(request)) {
final Resource resource = request.getResourceResolver().getResource(request.getResource().getPath());
// Your logic
modifications.add(Modification.onCreated(resource.getPath()));
}
}
protected boolean accepts(SlingHttpServletRequest request) {
return "/my/resource/type".equals(request.getResource().getResourceType());
}
}
I'm stuck with a redirection in Wicket (1.5) and the different versiont of the setResponsePage.
I mount a page with parameters, but I cant use the version with the class sinc I want to use a specific constructor to create this page in order to pass some arguments. When I do that, the generated url does not display the parameters.
Here is the code :
// WicketApplication
mount(new MountedMapper("create/${param}/full", MyPage.class));
// In a page
PageParameters parameters = new PageParameters();
parameters.add("param", "value");
// URL OK : create/value/full
setResponsePage(MyPage.class, parameters);
// URL KO : create//full
setResponsePage(new MyPage(parameters, arguments...));
Is there any way to set a custom response page with parameters and an instance of a page ? A way to do something like setResponsePage(new MyPage(parameters, arguments...), parameters);
Make these two changes:
Use overloaded constructors, one taking only PageParameters for when you need to pass parameters, and the other taking only an IModel for when you need to pass a model.
Change the $ to a # to make your param place holder optional and solve your URL issue:
mount(new MountedMapper("create/#{param}/full", MyPage.class));
I'm upgrading a custom solution where I can dynamically register and unregister Web Api controllers to use the new attribute routing mechanism. However, it seems to recent update to RTM break my solution.
My solution exposes a couple of Web Api controllers for administration purposes. These are registered using the new HttpConfigurationExtensions.MapHttpAttributeRoutes method call.
The solution also allows Web Api controllers to be hosted in third-party assemblies and registered dynamically. At this stage, calling HttpConfigurationExtensions.MapHttAttributeRoutes a second time once the third-party controller is loaded would raise an exception. Therefore, my solution uses reflection to inspect the RoutePrefix and Route attributes and register corresponding routes on the HttpConfiguration object.
Unfortunately, calling the Web Api results in the following error:
"No HTTP resource was found that matches the request URI".
Here is a simple controller that I want to use:
[RoutePrefix("api/ze")]
public sealed class ZeController : ApiController
{
[HttpGet]
[Route("one")]
public string GetOne()
{
return "One";
}
[HttpGet]
[Route("two")]
public string GetTwo()
{
return "Two";
}
[HttpPost]
[Route("one")]
public string SetOne(string value)
{
return String.Empty;
}
}
Here is the first solution I tried:
configuration.Routes.MapHttpRoute("ZeApi", "api/ze/{action}");
Here is the second solution I tried:
var type = typeof(ZeController);
var routeMembers = type.GetMethods().Where(m => m.IsPublic);
foreach (MethodInfo method in routeMembers)
{
var routeAttribute = method.GetCustomAttributes(false).OfType<RouteAttribute>().FirstOrDefault();
if (routeAttribute != null)
{
string controllerName = type.Name.Substring(0, type.Name.LastIndexOf("Controller"));
string routeTemplate = string.Join("/", "api/Ze", routeAttribute.Template);
configuration.Routes.MapHttpRoute(method.Name, routeTemplate);
}
}
I also have tried a third solution, whereby I create custom classes that implement IHttpRoute and trying to register them with the configuration to no avail.
Is it possible to use legacy-style route mapping based upon the information contained in the new routing attributes ?
Update
I have installed my controller in a Web Application in order to troubleshoot the routing selection process with the Web Api Route Debugger. Here is the result of the screenshot:
As you can see, the correct action seems to be selected, but I still get a 404 error.
Update2
After further analysis, and per Kiran Challa's comment below, it seems that the design of Web Api prevents mixing attribute routing and conventional routing, and that what I want to do is not possible using this approach.
I have created a custom attribute [RouteEx] that serves the same purpose of the Web Api [Route] attribute, and now my code works perfectly.
I guess, since this is not possible using the conventional attribute routing, none of the answers on this question could legitimately be consisered valid. So I'm not nominating an answer just yet.
You shouldn't be required to use reflection and inspect the attribute-routing based attributes yourself. Attribute routing uses existing Web API features to get list of controllers to scan through.
Question: Before the switch to attribute routing, how were you loading these assemblies having the
controllers?
If you were doing this by IAssembliesResolver service, then this solution should work even with attribute routing and you should not be needing to do anything extra.
Regarding your Update: are you calling MapHttpAttributeRoutes?
ISSUE
We just switched from MVC4 Web API Beta to the RC and we're running into a Multiple actions were found that match the request ... exception in our service.
BACKGROUND
We have two POST actions defined in our ApiController:
public class MyModelController : ApiController
{
...
// POST /mymodel
public MyModel Post(MyModel model)
{
...
}
// POST /mymodel/upload
[ActionName("Upload")]
public HttpResponseMessage UploadModelImage()
{
HttpRequestMessage request = Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.UnsupportedMediaType, request));
}
...
}
}
The first action (default POST action) is used to create a new MyModel object from the JSON passed to the service. The user of our portal has the option to upload an image as part of creating a new MyModel object in which case we use the second Upload action to save the file and persist the new object to the database. This action reads the multipart request content, parses out the properties for the model and saves the image uploaded to our CDN.
Since our switch to the RC, the upload action (http://www.myapidomain.com/mymodel/upload) goes through fine, but the regular POST action (http://www.myapidomain.com/mymodel/) fails with the Multiple actions were found that match the request ... exception citing both the methods listed above as the conflicts.
Here are our routes:
routes.MapHttpRoute(
"Default", // route name
"{controller}" // route template
);
routes.MapHttpRoute(
"OnlyId", // route name
"{controller}/{id}", // route template
new {}, // defaults
new {controller = #"[^0-9]+", id = #"[0-9]+"} // constraints
);
routes.MapHttpRoute(
"OnlyAction", // route name
"{controller}/{action}", // route template
new {}, // defaults
new {controller = #"[^0-9]+", action = ActionNameConstraint.Instance} // constraints
);
routes.MapHttpRoute(
"DependantAction", // route name
"{controller}/{principalId}/{action}/{dependentId}", // route template
new {dependentId = System.Web.Http.RouteParameter.Optional}, // defaults
new {controller = #"[^0-9]+", action = ActionNameConstraint.Instance} // constraints
);
ActionNameConstraint is just a custom constraint that ensures that the {action} must belong to the {controller}
QUESTION
I've tried messing with the routes in different orders to see if that would fix the issue with no luck. I'm looking for help with any of the following solutions:
A potential issue in our routes.
An alternative solution for routing by content-type. The Upload action only needs to be called for mult-part form posts. If the content type is JSON or XML, the regular action should be used. I haven't been able to find any resources that suggest this can be done, but I'm hoping someone else has considered this.
A model-binding approach for reading file streams from the request content so we don't need the separate Upload action anymore
By default it is not possible to mix REST style routing and RPC style routing in a single controller - which it seems you are trying to do.
There is an open issue for that on ASP.NET Web Stack's codeplex, where Web API source lives - http://aspnetwebstack.codeplex.com/workitem/184.
If you want to use it like that, you need to move the upload action to a separate controller, which will be called in an RPC-only way.