20

How can an OWIN startup method get the base URL of the web site?

I'm trying to write code that will work when debugging with IISExpress, unit testing with self hosting and under IIS.

When self hosting, I can find this information in IAppBuilder.Properties["host.Addresses"] but, it isn't there when running under IISExpress (haven't checked IIS).

abatishchev
  • 98,240
  • 88
  • 296
  • 433
John Vottero
  • 845
  • 1
  • 7
  • 24
  • Afaik, this is not possible via managed c# code. Simply because of the fact, that IISExpress and your assembly are compartmentalized in different processes, where the Owin assembly youve build is a child process of IISExpress. It is only capable of reading variables in the ENVIRONMENT. The 'bind' and 'listen' is handled through IISExpress (virtual paths, port and host filtering etc). Until you have a request at hand (then use middleware) you have no details about `Server`, `Request` nor `Response`. Ask if you need details – mschr Oct 25 '16 at 05:27

3 Answers3

3
System.Web.VirtualPathUtility.ToAbsolute("~")
SergeyA
  • 4,427
  • 1
  • 22
  • 15
  • 3
    That throws an exception when self hosting, the message is: "The application relative virtual path '~' cannot be made absolute, because the path to the application is not known." – John Vottero May 13 '15 at 21:24
1

I am keeping this post as an educational to illustrate not to go down the path I provided. It's easy to see how this answer could have been a solution to an untrained eye.

(EDIT: 10/6/2016)

Okay, so the conversation below this post was not helping me understand why I was wrong. So I've asked friends, coworkers, and finally received a good answer from the local community that I'm part of to explain the conversation more and why this answer wasn't sufficient. It was mentioned that the below does NOT answer the start up application request of the base URL, it answers how to retrieve fetching the base URL of the requested application using request handling. The difference is, the request handling fetches the base URL upon every request; whereas fetching the base URL from the start up application occurs only once and keeps the value of the requested application only when the application begins.

I honestly haven't seen or viewed any kind of documentation that allows you to retrieve the base URL outside of the current request handling scheme. I'm also not sure if it's even possible in the current state of the .NET stack. So again, I do apologize for not pointing this out and getting confused about the solution.

For those of you who still want to use the workaround in fetching the base url from a requested application (which may be from the startup application.. or some other kind of external application), and don't mind fetching it per request, and doesn't mind what point later in time that the base URL will be retrieved, the solution below answers that.

(Original Solution)

Here's a nice article that explains using Owin middleware pipeline:

http://blog.2mas.xyz/owin-middleware/

You can use app.Run which accept a context, or app.Use which accepts a context, and next which is of type Func (getting the next step in the pipeline).

public void Configuration(IAppBuilder app)
{
    JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
    //First way using app.Use
    var currentUri1 = "";
    app.Use((context, next) => { 
        currentUri1 = context.Request.Uri.ToString(); //Get base URL
        return next().ContinueWith(task =>
        {
            context.Response.WriteAsync(" FINISHED RETRIEVING CURRENT URL ");
        });
    });
    
    //Second way using app.Run
    var currentUri2 = "";
    app.Run((context) => { 
        currentUri2 = context.Request.Uri.ToString(); //Get base URL
         var task = context.Response.WriteAsync("Hello world! " + context.Request.Path);
            return task;
    });
}

context.Request is essentially the wrapper of the incoming request, it's of type IOwinRequest. More info is found here: IOwinRequest Interface

The Uri property of IOwinRequest is of type System.Uri, so you can view what kind of properties (like host, port, absolute url, query variables, etc) that come with Uri detailed here: Uri Class

[EDIT in response to comment]

If you truly don't believe that context.Request is available in startup, check out this flow:

IOwinContext, which "wraps OWIN environment dictionary and provides strongly typed accessors", which has a property called IOwinRequest, which "Gets a wrapper exposing request specific properties." It's of type Microsoft.Owin.IOwinRequest, which essentially has the property Uri

Nimantha
  • 6,405
  • 6
  • 28
  • 69
sksallaj
  • 3,872
  • 3
  • 37
  • 58
  • 2
    I don't see how that helps get the base URL of the site. – John Vottero Oct 13 '15 at 17:10
  • What do you mean? context.Request.Uri.ToString() gives you exactly what you're looking for. You're making a call through the pipeline to fetch the request, and the request context pulls in whatever info you need. The above code shows TWO different ways of grabbing that info. – sksallaj Oct 13 '15 at 17:47
  • 3
    The Use and Run methods add handlers to the pipeline that get called when a request comes in. context.Request isn't available at startup. – John Vottero Oct 20 '15 at 14:14
  • Hmm, I'm questioning if you actually read the literature I gave you. The Use and Run don't add handlers to the pipeline, they are part of the Owin extensions library. The 'context' is a parameter in the lambda expression. This quote is from the literature I mentioned: "The Use method takes a Func as a parameter. Breaking it down gives us; an environment IOwinContext which is a wrapper over a IDictionary that contains all the data in the request and response." – sksallaj Oct 20 '15 at 16:59
  • 4
    Yes, I did read the literature. The Use and Run methods add handlers that are called when a request comes in. They are not called at startup. There isn't an IOwinContext available at startup. – John Vottero Oct 29 '15 at 21:52
  • So weird, because i was able to pull up the base URL right at startup, and IOwinContext is available.. using the exact same code I showed.. it's a direct copy and paste from the code I wrote being used in production. So I'm really not sure what you're doing wrong. Can you tell me what part of the code you're confused about? – sksallaj Nov 07 '15 at 01:17
  • 2
    @sksallaj But you're not [using the context during startup]. You're using it inside the handlers. I think you might be confused by the lambda syntax; the code "inside" isn't executed at the same time as the `Configuration` method (which should be obvious if you pause and think about what these `Use*`/`Run` methods mean). – tne Jan 08 '16 at 11:49
  • I know that. You must use these handlers to traverse the pipeline in order to fetch the context. I've tested this code, I'm currently using this code in production, and it gets me exactly what I need. I have no idea what the problem with this answer is. You can't fetch the info the OP is trying to get any other way. – sksallaj Jan 08 '16 at 21:14
  • 1
    app.Use or app.Run insert middleware into the Owin pipeline. They are NOT executed during the startup function execution. You are creating a closure over the variable which will be set during each request ( threading issues ?). The questioner wants to get the base URL at the time of the startup execution – Robert Slaney Aug 29 '16 at 22:27
  • Just curious, why does the timing of execution matter when you're trying to get a base URL? Would that ever change during the execution of the code app.Run or app.Use? – sksallaj Sep 07 '16 at 01:45
  • sorry @JohnVottero I understand what was being asked now. I was only thinking about the code being executed and the location of where you put it, not the timing of the request being made. I edited the solution to make a remark on that. – sksallaj Oct 06 '16 at 20:38
-1

For anyone under vNext, try this:

public void Configure(IApplicationBuilder app)
        {
            app.Use(async (ctx, next) =>
            {
                var hostingEnvironment = app.ApplicationServices.GetService<IHostingEnvironment>();
                var realPath = hostingEnvironment.WebRootPath + ctx.Request.Path.Value;

                // do something with the file

                await next();
            });

        }

If you're not under vnext, I did see this answer that did not work for me under vNext / dnx: https://stackoverflow.com/a/24747658/486028

Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase)

Under dnx this just game me the folder containing the .dnx runtime but might work under other contexts

Community
  • 1
  • 1
brendanrichards
  • 309
  • 2
  • 6
  • 2
    You had be excited for a minute because this: "app.ApplicationServices.GetService();" looks very promising. But, then I realized that it is part of the IApplicationBuilder interface which is ASP.NET vNext. I'm using the current version. – John Vottero Nov 04 '15 at 18:37
  • @john vottero, yes my code is for vnext. Have updated my answer to make this explicit. – brendanrichards Nov 06 '15 at 11:19