I am trying to work out how to use the Hooks and just can't seem to get the syntax correct.
I have built a site using PirahnaCMS that has a blog component and am extending it to call some social plugins and auto post to FB, Twitter etc.
I just can't seem to get the syntax correct though. My app is MVC and I have looked at this section
1.2 ASP.NET MVC
If you're using ASP.NET MVC hooks should be attached in you Global.asax.cs in the Application_Start method, or any other place where you keep you startup code. You attach you hooks with the followin syntax:
protected void Application_Start() {
Piranha.WebPages.Hooks.Menu.RenderItemLink = (ui, str, title, url) => {
str.Append(String.Format("<span>{1}</span>", url, title)) ;
} ;
}
The Hook I believe I want to use is Piranha.WebPages.Hooks.Manager.PostEditModelAfterSave but for the life of me I can't seem to work it out.
All of the hooks are just static delegates that you can attach methods to. In the above example an anonymous method has been assigned to the hook with the syntax:
delegate += (parameters) => { method body }
You could also assign a previously declared method.
delegate += MyMethod
Example skeletons for attaching hooks should be available in the Docs at the official site. If not you can find the hooks in the file:
~/WebPages/Hooks.cs
And all delegates in:
~/Delegates.cs
I hope these URL:s are correct as I'm typing from memory :)
Regards
Related
I'm new to asp.net Core 2.0.
When you Create a new ASP.net Core 2.0 MVC Project (Web Application (Model-View-Controller)), you get a HomeController with Index(), About() and Contact() methods as in MVC 5.
However you also get this:
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
I can't find any documentation in Google or in any books as to what this is for and what the purpose of the single line of code is.
Can somebody please explain it? Is it some kind of best practice - should we be using this?
Thanks.
If time permit, I'll try to update my answer. But for now, the brief explanation is that every public method in your controller is callable as an http endpoint. For instance, the index method in your HomeController renders your index.cshtml view, the About renders the about.cshtml view and so on. In particular the public IActionResult Error() method is called if you redirect the user to the action when an error occurred. Consequently, it returns an Error View containing the information about the error, such as the RequestId and other values bonded with the ErrorViewModel.
I hope this help for now.
We are moving from an on premise-like application to a multi tenant cloud application.
for my web application we made a very simple interface based on IPlugin, to create a plugin architecture. (customers can have/install different plugins)
public interface IWebPlugin : IPlugin
{
string ContentBaseUrl { set; get; }
}
We have some plugins that would normally be loaded in on startup. Now i'm migrating the code to load at the beginning of a request (the Register function is called on request start), and scope everything inside this request.
It's not ideal but it would bring the least impact on the plugin system for now.
I could scope the Container by making an AppHost child container which would stick to the request:
Container IHasContainer.Container
{
get
{
if (HasStarted)
return ChildContainer;
return base.Container;
}
}
public Container ChildContainer
{
get { return HttpContext.Current.Items.GetOrAdd<Container>("ChildContainer", c => Container.CreateChildContainer()); }
}
problem case
Now im trying to make plugins work that actually add API services.
appHost.Routes.Add<GetTranslations>("/Localizations/translations", ApplyTo.Get);
But this service is unreachable (and not visible in metadata). How do i make it reachable?
I see you execute the following in ServiceController AfterInit. Re-executing this still wouldnt make it work.
//Copied from servicestack repo
public void AfterInit()
{
//Register any routes configured on Metadata.Routes
foreach (var restPath in appHost.RestPaths)
{
RegisterRestPath(restPath);
//Auto add Route Attributes so they're available in T.ToUrl() extension methods
restPath.RequestType
.AddAttributes(new RouteAttribute(restPath.Path, restPath.AllowedVerbs)
{
Priority = restPath.Priority,
Summary = restPath.Summary,
Notes = restPath.Notes,
});
}
//Sync the RestPaths collections
appHost.RestPaths.Clear();
appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x));
appHost.Metadata.AfterInit();
}
solution directions
Is there a way i could override the route finding? like extending RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
Or could i restart the path compilation/caching? (would be enough for now that the service would be reachable tenant wide )
All configuration in ServiceStack should be contained within AppHost.Configure() and remain immutable thereafter. It's not ThreadSafe to modify ServiceStack's Static Configuration at runtime like trying to modify registered routes or Service Metadata which needs to be registered once at StartUp in AppHost.Configure().
It looks as though you'll need to re-architect your solution so all Routes are registered on Startup. If it helps Plugins can implement IPreInitPlugin and IPostInitPlugin interfaces to execute custom logic before and after Plugins are registered. They can also register a appHost.AfterInitCallbacks to register custom logic after ServiceStack's AppHost has been initialized.
Not sure if it's applicable but at runtime you can "hi-jack Requests" in ServiceStack by registering a RawHttpHandler or a PreRequestFilter, e.g:
appHost.RawHttpHandlers.Add(httpReq =>
MyShouldHandleThisRoute(httpReq.PathInfo)
? new CustomActionHandler((req, res) => {
//Handle Route
});
: null);
Simple answer seems to be, no. The framework wasn't build to be a run-time plugable system.
You will have to make this architecture yourself on top of ServiceStack.
Routing solution
To make it route to these run-time loaded services/routes it is needed to make your own implementation.
The ServiceStack.HttpHandlerFactory checks if a route exist (one that is registered on init). so here is where you will have to start extending. The method GetHandlerForPathInfo checks if it can find the (service)route and otherwise return a NotFoundHandler or StaticFileHandler.
My solution consists of the following code:
string contentType;
var restPath = RestHandler.FindMatchingRestPath(httpMethod, pathInfo, out contentType);
//Added part
if (restPath == null)
restPath = AppHost.Instance.FindPluginServiceForRoute(httpMethod, pathInfo);
//End added part
if (restPath != null)
return new RestHandler { RestPath = restPath, RequestName = restPath.RequestType.GetOperationName(), ResponseContentType = contentType };
technically speaking IAppHost.IServiceRoutes should be the one doing the routing. Probably in the future this will be extensible.
Resolving services
The second problem is resolving the services. After the route has been found and the right Message/Dto Type has been resolved. The IAppHost.ServiceController will attempt to find the right service and make it execute the message.
This class also has init functions which are called on startup to reflect all the services in servicestack. I didn't found a work around yet, but ill by working on it to make it possible in ServiceStack coming weeks.
Current version on nuget its not possible to make it work. I added some extensibility in servicestack to make it +- possible.
Ioc Solution out of the box
For ioc ServiceStack.Funq gives us a solution. Funq allows making child containers where you can register your ioc on. On resolve a child container will, if it can't resolve the interface, ask its parent to resolve it.
Container.CreateChildContainer()
What would be the easiest way to setup a request/response interceptor in ServiceStack that would execute for a particular service?
A request filter (IHasRequestFilter) works fine but a response filter (IHasResponseFilter) is not triggered if the service returns non 2xx status code. I need to retrieve the status code returned by the method as well as the response DTO (if any).
A custom ServiceRunner and overriding the OnBeforeExecute and OnAfterExecute methods seems to work fine but I find it pretty intrusive as the service runner need to be replaced for the entire application and I couldn't find a way clean way to isolate per functionality the tasks that need to be executed in those methods.
Is there some extension point in ServiceStack that I am missing that would allow me to execute some code before each service method and after each service method? A plugin would be ideal but how can I subscribe to some fictitious BeforeExecute and AfterExecute methods that would allow me to run some custom code?
UPDATE:
Just after posting the question I found out that global response filters are executed no matter what status code is returned by the service which is exactly what I needed. So one last question: Is it possible to retrieve the service type that will handle the request in a request filter? I need to check whether this service is decorated by some custom marker attribute.
I have found out a solution to my question about how to retrieve the service type in a custom request/response filter:
appHost.RequestFilters.Add((req, res, requestDto) =>
{
var metadata = EndpointHost.Metadata;
Type serviceType = metadata.GetServiceTypeByRequest(requestDto.GetType());
...
}
A custom ServiceRunner and overriding the OnBeforeExecute and OnAfterExecute methods seems to work fine but I find it pretty intrusive as the service runner need to be replaced for the entire application
Quick note, you can opt-in and choose only what requests should use a custom service runner, e.g:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(
ActionContext actionContext)
{
return useCustomRunner(actionContext.RequestType)
? new MyServiceRunner<TRequest>(this, actionContext)
: base.CreateServiceRunner<TRequest>(actionContext);
}
IHttpRequest has OperationName. I think thats what you are after.
i am adding a new method into CalEventLocalServiceImpl using hook...
my code is ..
public class MyCalendarLocalServiceImpl extends CalEventLocalServiceWrapper {
public MyCalendarLocalServiceImpl(CalEventLocalService calEventLocalService) {
super(calEventLocalService);
// TODO Auto-generated constructor stub
}
public List getUserData(long userId) throws SystemException{
DynamicQuery query=DynamicQueryFactoryUtil.forClass(CalEvent.class)
.add(PropertyFactoryUtil.forName("userId").eq(userId));
List deatils=CalEventLocalServiceUtil.dynamicQuery(query);
return deatils;
}
}
liferay-hook.xml:
<service>
<service-type>
com.liferay.portlet.calendar.service.CalEventLocalService
</service-type>
<service-impl>
com.liferay.portlet.calendar.service.impl.MyCalendarLocalServiceImpl
</service-impl>
</service>
my question is how to use getUserData from jsp file.
Can anybody help me out....
i think u didn't gt my question...i want list of events based on USERID from Calendar ...to achieve this task what i need to do??
I assume getUserData() is not overridden but a new method (can't look up currently). This is not what you can do when overriding a service. Instead you'd have to add a new Service and make it available to the portal.
Remember that a customized ("hooked") jsp is running in the portal classloader, while your overloaded service is running in the hook's classloader. Thus, if you create a new service and make the service.jar available to Liferay (e.g. on the global classpath) you can call it from JSPs. The interface of Liferay services can not be extended through an overloaded service.
In case getUserData() is already in the interface (as I said I can't look up currently), you just need to call the CalendarLocalServiceUtil from your jsp and it will be delegated to your wrapper.
Just to add to Olaf's answer and comments...
if you you want to extend CalEventLocalService service with just "getUsetData" and use it in one jsp than building your own service might be overkill. Simply put your code from "getUserData" in jsp. Otherwise follow Olaf's suggestions.
I have an HttpHandler that is run on a client page (cross domain, not on our IIS server, etc) and when they click on our embedded link it fires off the Handler on our server. So far everything is working normally.
I am now trying to use the System.Web.HttpContext.Session object but it is null. I am thinking it is null because we don't have a session until our HttpHandler is invoked? And multiple calls to the handler will create a new session per call? If this is the case did MS just disable the Session object when calling into a HttpHandler? Can anyone confirm this?
If this is the case, what do you do to maintain state between calls? Some sort of SQL based data object? A file?
TIA
Have your HttpHandler implement the IRequiresSessionState interface. It will enable session state use.
IRequiresSessionState can be found in the System.Web.SessionState namespace.
I think you have to implement the empty interface IReadOnlySessionState, so the context will be loaded.
edit to add:
According to Michael Morton's answer, you can also implement IRequiresSessionState, which will give you write access also to the Session object
using System;
using System.Web;
using System.Web.SessionState;
public class DownloadHandler : IHttpHandler, IReadOnlySessionState
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
context.Response.Write(context.Session["kmx"]);
}
}
try using the current context...
System.Web.HttpContext.Current.Session