Why Request.Url.Authority is returning the internal path and not my domain - c#

I'm working through the final issues of an application set to go live this week. I need help to either modify my code or explain to our hosters what they need to fix in the IIS/DNS configurations to make this code work as expected.
Here is the code:
public string BaseSiteUrl
{
get
{
var c = this.ControllerContext.RequestContext.HttpContext;
string baseUrl = c.Request.Url.Scheme + "://" + c.Request.Url.Authority
+ c.Request.ApplicationPath.TrimEnd('/') + '/';
return baseUrl;
}
}
I make a call to this in my Controller, to generate a url that gets persisted to a database.
It works fine when I run on my local machine. However, it does not work when it is run on the beta server.
Expected results on beta. On the beta server this is an application named dr405
https://beta.sc-pa.com/dr405/
The actual result on beta. (I changed the server/domain names to what you see in CAPS for security's sake)
http://SERVERNAME1.GROUP1.SUBGROUP.local/dr405/

I don't think you need the method you wrote. There is a UrlHelper class that adds extension methods. To get the base URL for your site you should be using the Content() method like this:
var baseUrl = Url.Content("~/");
In your example, it looks like the http://SERVERNAME1.GROUP1.SUBGROUP.local/dr405/ result is an internal host name. On your development machine the internal host matches your public facing one. Your hosting provider is unlikely to be able to modify this for you, especially if it's a shared hosting solution.

If you're behind a load balancer or similar, it might be worth checking server variables. In our case we do something like this:
string hostName = Request.Headers["x-forwarded-host"];
hostName = string.IsNullOrEmpty(hostName) ? Request.Url.Host : hostName;
This question I asked a while ago might be of interest:
Asp.net mvc 301 redirect from www.domain.com to domain.com

Due to time constraints, and the need to get this project out the door, I had to resort to hardcoding the main part of the url into the application. After I made the change I felt stupid for trying to make it dynamic in the first place. I mean how often should our domain name change?

Related

Link to html page not working in live server/Blazor Wasm App but is working locally in debug?

I am developing a Blazor WASM application and need to add some external links to e.g. a cookies page which I am putting in a seperate tab using:
<a href="/Legal/Cookies.html" class="b-bar-link" target="_blank">
Cookies
</a>
Obviously with a Web assembly blazor app, most of it is loaded on to the client, but I don't think I have quite got my head around objects that still live on the client vs the server.
Images etc. stay on the server in the wwwroot folder. So I thought I should be able to create a /Legal folder in wwwroot and put the files there. These docs might change often so I figured better there.
So my folders look like most wasm template projects:
When I use the link to get to the cookies page, it works fine in VS under debug, but in the live server is heading to the 'Page not found'.
I am using an Azure AppService, and initially thought that maybe the problem may be due to a publishing/build issue. So I checked and the file properties all have 'copy always' as their
content action. Also, I checked the server published files and they are there.
I realise that the VS debug environment is using a cut down IIS and is different to the main Azure one, so I wondered if it could be permissions related. Suggestions please.?
EDIT
I think this is as someone suggested a routing issue. But does anyone know how to have Blazor Wasm mixed with the occasional file on the server? I mean, the default Identity stuff that MS provides does that very thing, though it is hard to figure out... any pointers to a doc? Thanks.
You should look at this for hosting: https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/?view=aspnetcore-7.0&tabs=visual-studio#app-base-path
In many hosting scenarios, the relative URL path to the app is the root of the app. In these default cases, the app's relative URL base path is the following:
Blazor WebAssembly: / configured as <base href="/" />.
Blazor Server: ~/ configured as <base href="~/" />.
In other hosting scenarios, such as GitHub Pages and IIS sub-apps, the app base path must be set to the server's relative URL path of the app.
See the rest of the article for how to adapt to your scenario. In short, you can include your path in launchSettings.json and have different configurations based on your dev/production environments.
Also take a look at the following article for absolute url handling inside your app if required: https://swimburger.net/blog/dotnet/how-to-generate-absolute-urls-in-aspdotnet-core
I am answering my own question here in case anyone else falls into this particular set of traps..
I was guided to some extent by a sort of similar question:
blazor-listing-and-linking-local-html-files-from-blazor-page
It wasn't a routing problem as such, more a mis-understanding on my part of how a Blazor app is structured, and that it can't easily switch in an out of being a Single Page App...
Anyway, I am using Blazorise to help with my UI so I constructed a 'Popup' page to handle this sort of thing, like this:
<Modal #ref="modalRef">
<ModalContent IsCentered="true" Size="ModalSize.ExtraLarge">
<ModalHeader>
<CloseButton Clicked="#HideModal" />
</ModalHeader>
<ModalBody Style="min-height: 50vh">
#htmlContent
</ModalBody>
</ModalContent>
</Modal>
This with the code page to activate the popup and get the data as follows:
[Inject] FileService _fileService { get; set; }
Modal? modalRef = new Modal();
[Parameter]
public string urlPath {get; set;}
private MarkupString htmlContent= new MarkupString();
private async Task GetContent()
{
htmlContent = (MarkupString) await _fileService.GetServerFileString(urlPath);
}
public async Task ShowModal(string? url = null)
{
if (!string.IsNullOrEmpty(url))
{
urlPath = url;
}
await GetContent();
modalRef.Show();
}
public void HideModal()
{
modalRef.Hide();
}
The _fileService function being:
public async Task<string> GetServerFileString(string serverURL)
{
string content = await HttpPublic.GetStringAsync(serverURL);
return content;
}
Then to use it in a page, (I am actually referencing straight from the MainLayout) it is as easy as:
<Button #onclick='() => ShowPopup("/Legal/Privacy.html")'>Privacy</Button>
or
<Button #onclick='() => ShowPopup("/Legal/Cookies.html")'>Cookies</Button>
With the code function being:
protected async void ShowPopup(string url)
{
await htmlPopup.ShowModal(url);
}
I did it that way because I am using the component several times in the same page... Given the Parameter function, you could also do it by just referencing the component and passing the param.
Hope this helps someone else. Cheers

ASP.NET and C#: where do you store the rest client url?

I have my rest client url hard-coded in my code-behind, but upon peer-reviewing my code, I was asked to move that url to the config file so that it can be changed for each environment.
Visual Studio 2019 now complains because my rest client url has an invalid = sign as token in the url itself, and it expects ; as token instead.
Has anyone ever come across this, and is it correct to move the rest client to the config file? In theory that should not change.
Can't share the full url, but the part that is highlighted as error is this: version=2.0&details=true.
I found the answer. The problem is in the & symbol itself. Once converted to &, the errors Visual Studio was highlighting were gone and my solution worked again.
If i will do that i will save in config file only base url like this
"WebConfig": {
"SmsCenterApi": "https://some_site.com/SendService"
}
and in code I can complete the link
string url = WebConficData.SmsCenterApi+"version=2.0&details=true";
andafter that I can use url to make some request. For multi-environments web.config and appsettings is avesome. You just change base url for each env and that's it.
I think the answer to your questions
where do you store the rest client url?
is it correct to move the rest url to the config file?
is dependent on how you implement the rest request to that url. As you do not show any information on how you implement the rest call, I would like to show you one possible way and hopefully give you an impression about which things you should consider when implementing a solution.
So we can basically (for the sake of completeness) split an rest-endpoint url into two parts which might affect our implementation.
The base url:
"https://www.some-nice-name.com/SomeEndPoint"
and the parameters
?key1=value1&key2=value2
having this in mind, you could go the way and split them up, storing the base url and the parameters in two different nodes/attributes in a config file:
{
"BaseUrl" : "https://www.some-nice-name.com/SomeEndPoint",
"UrlParams" : "?key1=value1&key2=value2"
}
Or in one node/attribute, or even split each single parameter pair ("key1=value1") into own fields. And so on, and so on......
Anyway, if we now jump into our C# code and implement the Rest call, we have a wide range of different possible solution. For this example I will show you how to use the RestSharp NuGet package and why it might influences our decision on the above question.
So one basic example:
// I will not show the implementation of ReadYourConfigStuff() because its fairly clear what should happen here
var config = ReadYourConfigStuff();
// Give the BaseUrl to our REST client
var restClient = new RestClient(config.BaseUrl);
// Just taking GET as we have some GET Parameters
var restRequest = new RestRequest(Method.GET);
so far so good. But we still miss our parameters right?
Let's go ahead:
// RestSharp gives us a very nice tool to add GET parameters to our request
restRequest.AddParameter("key1", "value1");
restRequest.AddParameter("key2", "value2");
That looks quite different to what we added to our config file, does it? Yes it does. As RestSharp gives us a tool at hand which allows to add parameters one by one, we are free to choose how to store and maintain those in our code. And if we have a look on the AddParameter defintion
public IRestRequest AddParameter(string name, object value);
we see that the second parameter can be any object. Amazing!
So my answer to your question is: Yes you can store it in the config file, but does it fit to your implementation? Are the parameters fix or do they change? How does your favorite tooling would like you to implement the rest request?
Based on the answers to these questions, I would take a decision rather to use a config file or not.

Instantiation of a controller in OnActionExecuting (...not throwing a 404) in MVC Azure

The objective is to add a maintenance batch on the same url of the administration of an Azure MVC site. The url should be something like:
https://admin.mysite.com/Batch?pass=HKE671
I decided to override OnActionExecuting and to capture the information I need in the url to trigger the maintenance method. I am not familiar with MVC projects, and this may not sound very conventional...
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
EmailUtility.SendSupportEmail("DEBUG - On result executing", ""); //I receive this email
int y = 14;
if (Request.Url.Query.Length > y)
{
string url = Request.Url.Query.Remove(0, y); // I remove ?ReturnUrl=%2f
if (url.StartsWith("Batch"))
{
mySite.Admin.Web.Controllers.TenantController controller = new mySite.Admin.Web.Controllers.TenantController();
EmailUtility.SendSupportEmail("DEBUG - starts maintenance", ""); // I don't receive this one
controller.maintenance(HttpUtility.ParseQueryString(url).Get("pass"));
};
}
base.OnActionExecuting(filterContext);
}
This code works as well as I need on local, the maintenance method is called and does the job. But when deployed on Azure, this modification throws a 404 error. Interestingly, I send two debug emails : I don’t receive the second one "DEBUG - starts maintenance", therefore my 404 error comes from the instantiation of a controller in OnActionExecuting.
First, I would like to understand why is the behavior different between my development machine and Azure?
Then, how can I make it work? Thanks,
EDITED on Jan 4:
I made a little progress, but this issue is still unsolved.
- About the difference between my dev machine and Azure: there are a few redirections on this site: https, 404 and non-existent domain. I assumed it was due to a 404 error. Encapsulating the code with a try/catch didn't sent me any error, so I am guessing that I can suppress the 404 from the hypothesis.
- I tried the code above on OnAuthorization without having more success.
- I noticed that the first email DEBUG - On result executing is in fact sent at the first test only. It is not sent the second time I run my test. This doesn't make any sense to me, because the session should be checked every time.
Conclusion for today: it seems to be more a routing/redirection problem.
Why don't you do:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
try {
//your code here
} catch (Exception ex){
EmailUtility.SendSupportEmail("Execution failed: " + ex.Message , "");
}
}
If your code is throwing an exception, this should give you a better understanding on why it is failing.
But more important, a more robust way of checking for the correct URL would be:
if (filterContext.HttpContext.Request.Url.AbsolutePath.EndsWith("Batch")){
}
As far as i can tell your string variable "url" will not ever start with "Batch", because Request.Url.Query only conains the part after the "?" thus making your check always return false. Why this IS working on localhost is hard to tell, but in my opinion it shouldn't.
Actually, to work in all cases your check should be:
if (filterContext.HttpContext.Request.Url.AbsolutePath.ToLower().EndsWith("batch") || filterContext.HttpContext.Request.Url.AbsolutePath.ToLower().EndsWith("batch/")){
}
After your edit:
Ok, so what's happening is that you're requesting the action "Batch" in Home Controller, but because you're not authenticated you are redirected to LogOn in Account Controller. You should be seeing a login page, unless there's no associated LogOn view in your Views folder, or no AccountController at all, in both cases an exception is thrown.
Is the Batch method supposed to check for authentication? If not, remove the [Authorize] annotation from this method in HomeController and you should be fine.
You still have to adjust the if-check though, because it actually only evaluates to true on the LogOn page, and false if you actually get to the Batch page.
Shame on me, the problem was somewhere else!... The code above works well, but the routing of this site rejected an incomplete url. In this case, it had to contain the project name: https://admin.mysite.com/Admin/Batch?pass=HKE671 Then all is back to normal!
So the answer is: the behavior is different on my local computer because the local host of my development machine doesn't route projects of the solution the same way Azure does.
Many thanks for your help!

How can I make url path in Swashbuckle/Swaggerwork when api is served from inside another project?

all. I am trying to document a WebApi 2 using Swashbuckle package.
All works great if the API is running by itself i.e. localhost/api/swagger brings me to ui and localhost/api/swagger/docs/v1 to json.
However the producation app initializes this same Webapi project by running webapiconfig method of this project from global.asax.cs in another - now web project (the main application one). So the api url looks like localhost/web/api instead of localhost/api.
Now swashbuckle doesn't work like that at all.
localhost/api/swagger generates error cannot load
'API.WebApiApplication', well of course
localhost/web/swagger = 404
localhost/web/api/swagger = 404
I tried to look everywhere, but all I found is workaround.
c.RootUrl(req => req.RequestUri.GetLeftPart(UriPartial.Authority) + VirtualPathUtility.ToAbsolute("~/").TrimEnd('/'));
Unfortunately it doesn't work, now maybe it should and I just need to change something but I don't even know what exactly this property expects and what it should be set to.
May be it's not even applicable - maybe setup we have requires something else or some swashbuckle code changes.
I will appreciate any help you can provide. I really starting to like swagger (and swashbuckle) for rest documentation.
For Swashbuckle 5.x:
This appears to be set by an extension method of httpConfiguration called EnableSwagger. Swashbuckle 5.x migration readme notes that this replaces SwaggerSpecConfig. SwaggerDocConfig RootUrl() specifically replaces ResolveBasePathUsing() from 4.x.
This practically works the same as it did before, looks like the biggest change was that it was renamed and moved into SwaggerDocConfig:
public void RootUrl(Func<HttpRequestMessage, string> rootUrlResolver)
An example from the readme, tweaked for brevity:
string myCustomBasePath = #"http://mycustombasepath.com";
httpConfiguration
.EnableSwagger(c =>
{
c.RootUrl(req => myCustomBasePath);
// The rest of your additional metadata goes here
});
For Swashbuckle 4.x:
Use SwaggerSpecConfig ResolveBasePathUsing and have your lambda read your known endpoint.
ResolveBasePathUsing:
public SwaggerSpecConfig ResolveBasePathUsing(Func<HttpRequestMessage, string> basePathResolver);
My API is behind a load balancer and this was a helpful workaround to providing a base address. Here's a dumb example to use ResolveBasePathUsing to resolve the path with a known base path.
string myCustomBasePath = #"http://mycustombasepath.com";
SwaggerSpecConfig.Customize(c =>
{
c.ResolveBasePathUsing((req) => myCustomBasePath);
}
I hardcoded the endpoint for clarity, but you can define it anywhere. You can even use the request object to attempt to cleanup your request uri to point to /web/api instead of /api.
The developer commented on this workaround on GitHub last year:
The lambda takes the current HttpRequest (i.e. the request for a given
Swagger ApiDeclaration) and should return a string to be used as the
baseUrl for your Api. For load-balanced apps, this should return the load-balancer path.
The default implementation is as follows:
(req) => req.RequestUri.GetLeftPart(UriPartial.Authority) + req.GetConfiguration().VirtualPathRoot.TrimEnd('/');
...
Re relative paths, the Swagger spec requires absolute paths because
the URL at which the Swagger is being served need not be the URL of
the actual API.
...
The lambda is passed a HttpRequestMessage instance ... you should be able to use this to get at the RequestUri etc. Another option, you could just place the host name in your web.config and have the lambda just read it from there.

Passing parameters from silverlight to ASP.net

I've made a little game in silverlight that records users scores whilst they play.
I decided it would be a lot better if I could implement a leaderboard, so I created a database in mySQL to store all the high scores along with names and dates. I have created some communications to the database in ASP.net. This works and I can simply insert and get data within the code.
It's now time to link the silverlight project with the ASP.net database communications, so I can send the users name and score as variables to my ASP.net code and then it will upload it to the database. That's all I need. Surely there must be an easy way of doing this, I just can't seem to find any ways when researching.
Thanks in advance,
Lloyd
At first you need add Generic Handler to your ASP.Net project.
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string userName = context.Request["user"];
int score = int.Parse(context.Request["score"]);
//And store it in DB
}
}
After you need call this handler from SilverLight app:
string uri = HtmlPage.Document.DocumentUri.ToString();
// Remove the web page from the current URI to get the root URI.
string rootUri = uri.Remove(uri.LastIndexOf('/'),
uri.Length - uri.LastIndexOf('/'));
string diggUrl = String.Format(rootUri + "/" + "test.ashx?user={0}&score={1}", "testuser", "234");
// Initiate Async Network call to Digg
WebClient diggService = new WebClient();
diggService.DownloadStringAsync(new Uri(diggUrl));
here i used Uri Class to send parameter to asp.net, but you can send string format only.
// this code written on Silverlight Button Click Event.
Uri myURI = new Uri(HtmlPage.Document.DocumentUri,String.Format("Report.aspx?brcd={0}&acc={1}&user={2}", Brcd, Acc, User)); HtmlPage.Window.Navigate(myURI, "_blank");
below code is written on Asp.net page_load or page init event
Brcd = Request.QueryString["brcd"];// brcd value accept here.
acc= Request.QueryString["ACC"];`
user= Request.QueryString["User"];
in above code we accept the silverlight parameter in asp.net but in [] bracket put name as it is use in silverlight page because it case sensitive.
By ASP.NET, do you mean an ASP.NET Webforms app?
If so, an ASP.NET Webforms app is a method of building a UI. What you need is an API, for your Silverlight app to use programatically. For this purpose you may want to consider building an ASP.NET Webservice instead, which provides an API over HTTP.
What do you need its to send data to web server from a Silverlight application, right?
You can:
Call Javascript functions from Silverlight and, there, do a postback
Call web services with Silverlight, but make sure its in same server which your SL application came from, or you will face some XSS issues.
An easy way to do this is to have your Silverlight code create a REST URL by encoding the information into the query string, and invoking an .aspx page on the server. The page wouldn't need to return any markup; it would just handle the back-end stuff and return.
Alternatively, you could make a web service call from Silverlight to your back end.
I prefer the latter approach. It's a little more work the first time through, but it's also more general purpose and makes for generally better code in the long run.
Although technically you could use JavaScript, I wouldn't suggest it; why go backwards in tech if you don't have to?

Categories

Resources