HttpRoutes - how do they work? - ajax

I´m struggling with URLs for ajax-reader/JSON. Each time I think I understand it, it seems that I haven´t.
Please, can anybody explain the logic behind this???
I got this Controller:
public class ServiceController : DnnApiController
{
[AllowAnonymous]
[HttpGet]
public HttpResponseMessage GetAllItems(int moduleId)
{
MyProjectController controller = new MyProjectController();
IEnumerable<ItemInfo> items = controller.GetAllItems(moduleId);
return Request.CreateResponse(HttpStatusCode.OK, items);
}
}
I got this Routemapper:
public class RouteMapper : IServiceRouteMapper
{
public void RegisterRoutes(IMapRoute mapRouteManager)
{
mapRouteManager.MapHttpRoute("MyProject",
"default",
"{controller}/{action}",
new[] { "MyCompany.MyProject.Services" });
}
}
At what URL can I read the data with $.ajax() and what is the URL showing me the data in a browser?
Thanx in Advance!
Asle :)

This is how I do it (Note: this will only work with DNN6.2 and above);
In the View.ascx.cs add
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
ServicesFramework.Instance.RequestAjaxScriptSupport();
ServicesFramework.Instance.RequestAjaxAntiForgerySupport();
jQuery.RequestDnnPluginsRegistration();
}
This ensures that jquery and the required DNN ajax plugins are added.
Initiate the services framework jquery plugin in the View.ascx like this inside javascript script tags (S.O. wouldn't allow me to include them)
var modId = <%=ModuleId %>;
var sf = $.ServicesFramework(modId);
Now in a separate javascript file or in the view.ascx control add the ajax function
function getAllItems(){
$.ajax({
type:"GET",
url:sf.getServiceRoot("MyProject")+"Service/GetAllItems",
beforeSend:sf.setModuleHeaders,
data:{moduleId:modId},
cache:false
}).done(function(data){
alert("Success!");
}).fail(function(){
alert("Crashed!");
}).always(function(){
//something you want done whether passed or failed
//like hide progress bar, ajax spinner etc.
});
}
The DNN jquery plugin will build the url which will look similar to this (Note: 142 is just for illustration purpose and will be replace with actual module id)
/DesktopModules/MyProject/API/Service/GetAllItems?moduleId=142

The URL will be something like
/desktopmodules/SlidePresentation/API/SlidePresetnation.ashx/ListOfSlides
I have examples at
https://slidepresentation.codeplex.com/SourceControl/latest
but they were for DNN6, they might require a few updates due to the API changes for DNN 7
you can see a DNN7 module that has a service layer at https://dnnsimplearticle.codeplex.com/SourceControl/latest#cs/services/

Related

How can i override placeOrder() action in Magento 2

I'm newbie in Magento. My shop should work with a web service. I have to check availability of products from web service before magento creates a new order. And after creating order successful i have to send the orderId back to web service. All this actions should be execute when a customer confirm a button "place order".
In a picture you see an "Place Order". I not sure how Magento does create a new order. I assume that an action placeOrder() will be call. My aim is to put a method checkAvailability() before this action and and method sendOrderId() after this action. checkAvailability() and SendOrderId() are the methods from webservice.
Has somebody an idea, how and where can i do that?
Sorry about bad english. Thank you
If you need to overwrite a function instead a class method (I used to overwrite Magento_Checkout/js/action/place-order).
requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/action/place-order': {
'My_Module/js/action/place-order': true
}
}
}
};
place-order.js
define(['mage/utils/wrapper'], function (wrapper) {
'use strict';
return function (placeOrderAction) {
return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, redirectOnSuccess) {
// my own code here
return originalAction(paymentData, redirectOnSuccess);
});
};
});
For your requirement, you need to used this event.
Used this event observer to check checkAvailability()
checkout_onepage_controller_success_action
Used this event observer to used SendOrderId()
sales_order_place_after
I had a similar case. I needed to override placeOrder action that was announced in third part module (Amasty_Checkout).
So, my solution was to create mixin in my theme.
1) Announce the mixin in theme with myTheme/Amasty_Checkout/requirejs-config.js:
var config = {
config: {
mixins: {
'Amasty_Checkout/js/view/onepage': {
'Amasty_Checkout/js/view/onepage-extend': true
}
}
}
};
2) Add mixin myTheme/Amasty_Checkout/web/js/view/onepage-extend.js with code:
define(
[
'jquery',
'uiComponent',
'ko',
'uiRegistry',
'Magento_Checkout/js/model/quote',
'Amasty_Checkout/js/action/set-shipping-information',
'Amasty_Checkout/js/model/agreement-validator',
'Amasty_Checkout/js/model/agreement-validator-old',
'Magento_Checkout/js/model/payment/additional-validators',
'Amasty_Checkout/js/model/amalert',
'mage/translate'
],
function (
$,
Component,
ko,
registry,
quote,
setShippingInformationAction,
checkoutValidator,
checkoutValidatorOld,
additionalValidators,
alert,
$t
) {
'use strict';
var mixin = {
placeOrder: function () {
// Here you put your extended code
}
};
return function (target) { // target == Result that Magento_Ui/.../default returns.
return target.extend(mixin); // new result that all other modules receive
};
});
Note that in my case I copied all content in define[...] section from original module script ('Amasty_Checkout/js/view/onepage') that I needed to override.
Here is the resource that helped me with my solution https://github.com/magento/magento2/issues/1864#issuecomment-141112927
I hope this will help someone save time.

Viewcomponent alternative for ajax refresh

I have a viewcomponent that contains some reusable business logic that embed in various pages. This has been working fine. However, I now have a requirement to refresh the viewcomponent using ajax.
Is there any way to accomplish this? From what I have read, it is not possible, although that info was a bit outdated.
If it is not possible, what is the best alternative?
On beta7 it is now possible to return a ViewComponent directly from a controller. Check the MVC/Razor section of the announcement
The new ViewComponentResult in MVC makes it easy to return the result
of a ViewComponent from an action. This allows you to easily expose
the logic of a ViewComponent as a standalone endpoint.
So you could have a simple view component like this:
[ViewComponent(Name = "MyViewComponent")]
public class MyViewComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
var time = DateTime.Now.ToString("h:mm:ss");
return Content($"The current time is {time}");
}
}
Create a method in a controller like:
public IActionResult MyViewComponent()
{
return ViewComponent("MyViewComponent");
}
And do a better job than my quick and dirty ajax refresh:
var container = $("#myComponentContainer");
var refreshComponent = function () {
$.get("/Home/MyViewComponent", function (data) { container.html(data); });
};
$(function () { window.setInterval(refreshComponent, 1000); });
Of course, prior to beta7 you could create a view as the workaround suggested by #eedam or use the approach described in these answers

Restricting auto Help Page contents when using Attribute Routing in Web API 2

I'm currently implementing a Web API using Web API 2's attribute routing (http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2). I am also using the Help Pages module in order to automatically generate documentation from XML comments (http://www.asp.net/web-api/overview/creating-web-apis/creating-api-help-pages).
For this API I am providing support for optional return format extensions, so that every API method has a pair of routes defined on it like so:
[HttpGet]
[Route("Path/Foo")]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFoo()
{
// Some API function.
}
This allows a user to hit any of these and get a result:
www.example.com/api/Controller/Path/Foo
www.example.com/api/Controller/Path/Foo.json
www.example.com/api/Controller/Path/Foo.xml
My issue is that when Help Pages uses MapHttpAttributeRoutes() to generate documentation, it is picking up both routes for each method. So right now I see help for:
api/Controller/Foo
api/Controller/Foo.{ext}
But I want to only see:
api/Controller/Foo.{ext}
I would prefer to hide the non-extension route on each method, so that every method only shows a single Help Page entry.
Has anyone else tried something similar? Is there a work around that I am missing?
My question would be is that, would consumers of your api figure out easily that the {ext} is optional?...personally, I would prefer the default behavior...but anyways following are some workarounds that I can think of:
A quick and dirty workaround. Split the DoFoo into 2 actions like DoFoo() and DoFooWithExt maybe. Notice that I am using an attribute called ApiExplorerSettings, which is for HelpPage purposes. Example below:
[HttpGet]
[Route("Path/Foo")]
[ApiExplorerSettings(IgnoreApi=true)]
public HttpResponseMessage DoFoo()
{
return DoFooHelper();
}
[HttpGet]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFooWithExt()
{
return DoFooHelper();
}
private HttpResponseMessage DoFooHelper()
{
//do something
}
Create a custom ApiExplorer (which HelpPage feature uses internally) and check for specific routes like the following and can decide whether to show the action or not for that particular route.
// update the config with this custom implementation
config.Services.Replace(typeof(IApiExplorer), new CustomApiExplorer(config));
public class CustomApiExplorer : ApiExplorer
{
public CustomApiExplorer(HttpConfiguration config) : base(config)
{
}
public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route)
{
if (route.RouteTemplate.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return base.ShouldExploreAction(actionVariableValue, actionDescriptor, route);
}
}
Get list of all ApiDescription from the default ApiExplorer and then filter out the descriptions which you do not like. Example:
Configuration.Services.GetApiExplorer().ApiDescriptions.Where((apiDesc) => !apiDesc.RelativePath.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))

Signalr has no connetions

I'm trying to use signalr in ApiController that handles upload from a page.
Essentially I am invoking my Hub from my uploadController and then I want to talk to my client.
shortened down the upload controller looks like this:
public class UploadController : ApiController
{
public Task<HttpResponseMessage> PostFile()
{
var hubMan = new HubManager();
hubMan.showUpload("test");
}
}
I then have my HubManager that should take care of sending to my client:
public class HubManager : Hub
{
public HubManager()
{
}
public void showUpload(string src)
{
Caller.showUpload(src);
}
}
I also tried this in my showUpload:
public void showUpload(string str)
{
var context = GlobalHost.ConnectionManager.GetHubContext<HubManager>();
context.Clients[this.Context.ConnectionId].showUpload(str);
}
My client side code looks like this:
$(document).ready(function () {
var progress = $.connection('/signalr/hubs/hubManager');
progress.showUpload = function (src) {
alert(src);
};
// Start the connection
$.connection.hub.start();
});
Now the problem is that in my Hub class everything is Null.
My Caller,Clients and Context is null. So it seems something is not initialising properly.
Any suggestions?
You cannot create an instance of a SignalR hub yourself. Hubs need to be initialized by SignalR so the properties like Clients etc. are available.
If you want to broadcast to clients from outside the hub (e.g. your controller), you need to use the context object of the hub as described in the wiki:
public ActionResult ControllerAction()
{
var context = GlobalHost.ConnectionManager.GetHubContext<HubManager>();
context.Clients[ /* connectionId or group name */ ].showUpload();
// ...
}
Please note that you can't access context.ConnectionId or context.Caller there, because you are calling your ApiController and not SignalR, so the framework knows nothing about the ConnectionId in this case. You need to pass it to the controller in some other way, e.g. cookies or as a parameter of the controller action.

Are there page checkers in WATIN which execute on each page load?

I am writing automation scripts using WATIR and WATIN. Watir has something called page checkers, which are code snippets that run on each page load. Is there something similar in WATIN ? I want a piece of code to run on each page load. Generally this is used to check for page errors or page loading images.
It is not really that easy to tell when page loads. I quickly googled about that page checkers in Watir, that you mentioned and found an article about page checkers in Watir. See first comment bellow the article. AFAIK it's really similar in WatiN.
Unfortunately, I don't see any similar functionality in WatiN (no event is fired after internal call to WaitForComplete. The easiest thing you could do is to subclass eg. IE class:
class MyIE : IE
{
public MyIE(string url) : base(url) { } //TODO: add constructors
public override void WaitForComplete(int waitForCompleteTimeOut)
{
base.WaitForComplete(waitForCompleteTimeOut);
Console.WriteLine("Page has been loaded");
}
}
However, the situation will be similar to described in mentioned comment (runs a lot more regularly than just page load).
I think that better approach would be using Page class from WatiN library. It is well documented. Example for watin.org webpage:
var ie = new MyIE("http://watin.org/");
var homePage = ie.Page<HomePage>();
Console.WriteLine(homePage.FirstFeature);
homePage.DocumentationLink.Click();
var documentationPage = ie.Page<DocumentationPage>();
Console.WriteLine(documentationPage.FAQLink.Url);
To run that code you need following classes:
abstract class WatiNBasePage : Page
{
[FindBy(Id = "header")]
public Div HeaderDiv { get; set; }
public Link HomeLink { get { return HeaderDiv.Link(Find.ByText("Home")); } }
public Link DocumentationLink { get { return HeaderDiv.Link(Find.ByText("Documentation")); } }
protected override void InitializeContents()
{
base.InitializeContents();
VerifyDocumentProperties(UnverifiedDocument, errorMessage => { throw new Exception(errorMessage); }); //TODO: modify if needed
}
protected override void VerifyDocumentProperties(Document document, Page.ErrorReporter errorReporter)
{
base.VerifyDocumentProperties(document, errorReporter);
if (!HomeLink.Exists)
errorReporter("HomeLink not exists");
//TODO: more checks here
}
}
class HomePage : WatiNBasePage
{
[FindBy(Id = "features")]
public Table FeatureTable { get; set; }
public string FirstFeature { get { return FeatureTable.Span(Find.First()).Text; } }
}
class DocumentationPage : WatiNBasePage
{
[FindBy(Text = "Frequently Asked Questions")]
public Link FAQLink { get; set; }
}
Basically you need to implement VerifyDocumentProperties. Above code will check if HomeLink exists, but maybe you would like to check if DocumentationLink exists etc. The second thing is to modify call to VerifyDocumentProperties. Now, if verification fails, Exception will be thrown after calling ie.Page<T>() (where T is a subclass of WatinBaseClass).
In my opinion, even if you don't need to use "page checkers", using Page class is still really useful and clarifies the code, so I really recommend using it. I regret that I haven't discovered it when I was starting work with WatiN.

Resources