Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Dan 1285 posts 3917 karma points c-trib
    Apr 05, 2020 @ 13:43
    Dan
    0

    Get website root in multi-site instance in a controller

    Hi,

    I have a multi-site instance of Umbraco (v7). Each website has a different domain name bound to it. I'd like to be able to access the IPublishedContent of the requested website route inside an API controller. So I'd like to return some contextual data for the requested website by calling the API's like this:

    I'm able to get the overall Content root no problem, but in my example I need the root of the particular domain being requested.

    I thought this might work:

    var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
    var root = umbracoHelper.AssignedContentItem.Site();
    

    ...but it seems that without it being a proper front-end request it can't route to the correct part of the content tree. It errors as follows:

     Cannot return the IPublishedContent because the UmbracoHelper was constructed with an UmbracoContext and the current request is not a front-end request.
    

    Can anyone advise how to get contextual content from an API in a multi-site instance?

    Thanks folks.

  • Yakov Lebski 553 posts 2117 karma points
    Apr 05, 2020 @ 19:08
    Yakov Lebski
    0

    In case what you call controller from Umnraco page AssignedContentItem can work, in case that you doing ajax call or calling you controller directly, AssignedContentItem will be not exists, becosue it not related to any content item. You can use Umbraco.TypedContentAtRoot or access page by Id

  • Steve Morgan 1346 posts 4453 karma points c-trib
    Apr 06, 2020 @ 08:42
    Steve Morgan
    0

    Hi,

    As you don't have context on each API call (it could come from anywhere) you probably need to add something to the call so you can determine which site to serve content from.

    I'd probably use the Guid of the homepage - you could use the ID but this is a bit more obscure to anyone have a snoop around the site (e.g. tryign to inject different IDs). You could also check that the typed content comes back is a homepage node as a check.

    namespace UmbracoV7_15_4.Controller
    {
        public class TestApiController : UmbracoApiController
        {
            public IEnumerable<string> GetAllProducts(Guid siteKey)
            {
                var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
                var root = umbracoHelper.TypedContent(siteKey);
    
                // check the site key passed is a root / homepage for security?
                if (root != null && root.DocumentTypeAlias == "homepage")
                {
                    var outputList = new List<string>();
                    outputList.Add(root.Id.ToString());
                    outputList.Add(root.Name);
    
                    return outputList;
                }
                else
                { 
                    return new List<string>();
                }
            }
    
        }
    }
    

    In this simple example you can pass the sitekey in the url:

    http://local.umbraco-b/Umbraco/Api/TestApi/GetAllProducts?siteKey=1b299884-375e-4a98-84fe-2bbec9701a05

    but you might want to have a generic APIRequest model that has this and inherit from this for all request models you attempt to bind.

    HTH

    Steve

  • Dan 1285 posts 3917 karma points c-trib
    Apr 06, 2020 @ 08:54
    Dan
    100

    Thanks all. In my case it's hard to know the id or udi of the content that's being requested, as the API will be called from a completely separate place that's unconnected to the endpoint. What I've done is just used the olden-but-golden alternative template approach, whereby I can just feed out json from the template and the context of the request is known. I'm convinced that there must be a way to do this from a proper controller but I've drawn a blank and the alt template works perfectly well - it just feels a bit hacky.

  • Steve Morgan 1346 posts 4453 karma points c-trib
    Apr 06, 2020 @ 09:46
    Steve Morgan
    0

    Hi Dan,

    You would always pass the same homepage guid for the website in my example. So all requests would go just need the same url param - not specific to each bit of content - just specific to that domain.

    I guess the only other way of doing it would be to match the domain on the request.

            public IEnumerable<string> GetAllProductsWithoutKey()
        {
            var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
    
            var domain = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
    
            var matchedRoot = umbracoHelper.TypedContentAtRoot().Where(x => x.UrlWithDomain().StartsWith(domain)).FirstOrDefault();
    
            var outputList = new List<string>();
            outputList.Add("Matched:" + matchedRoot.Id + " : " + matchedRoot.Name);
    
            return outputList;
        }
    

    * This probably needs to be checked for case insentitive matches and all sorts of other edge cases *

  • Dan 1285 posts 3917 karma points c-trib
    Apr 06, 2020 @ 10:10
    Dan
    0

    Thanks Steve. In my case I don't know the id or the udi of any of the content at all, not even the root of each domain, so passing in an identity isn't going to work unfortunately. Otherwise that would absolutely be the approach and would be no problem at all.

    In order to find the content node by matching the domain in a query feels like it could be a little fragile (as you say, lots of edge cases) and actually could be quite heavy in this situation. I can't seem to find any more 'core' way of doing this, so I think I'll stick with the alt template for now as it feels like the lesser of two evils. Thanks very much for your input though — much appreciated :)

  • Yakov Lebski 553 posts 2117 karma points
    Apr 06, 2020 @ 11:41
    Yakov Lebski
    0

    You don't need use inside UmbracoController

     var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
    

    just

    Umbraco
    
Please Sign in or register to post replies

Write your reply to:

Draft