NSwag - Incorrect Auto Generated "BaseURL"(Host) In Swagger Services - c#

I have a C# WebForms application where I'm using swagger initialized in my Global.asax in the following way:
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapOwinPath("swagger", app =>
{
app.UseSwaggerUi3(typeof(Global).Assembly, settings =>
{
settings.MiddlewareBasePath = "/swagger";
settings.DocumentTitle = "API";
settings.GeneratorSettings.DefaultUrlTemplate = "api/{controller}/{action}/{id}";
settings.GeneratorSettings.Title = "API";
settings.GeneratorSettings.Description = "REST interface for API";
settings.GeneratorSettings.UseControllerSummaryAsTagDescription = true;
});
});
}
I often deploy to a app pool and website that supports may different URL's for the various instances I'll have:
https://testci1.ABC.com
https://testci2.ABC.com
etc..
I've noticed that when I navigate to the swagger URL on one of my websites that often times the URL it wrong, for instance, when I navigate to "https://testci2.ABC.com/swagger" I see a "BaseURL" of:
[ Base URL: testci1.ABC.com ]
Which is wrong, and causes all my swagger services call to fail due to IIS security around cross URL requests.
There is a great deal of discussion on this topic, but very little specific to C#. How to I get NSwag to generate a base URL("host" technically) from I've navigated to, it seems to be caching and/or incorrectly generating and storing the wrong baseURL?

Related

Multiple API endpoints based on Modules

I am using ASP.NET Boilerplate v2.0.1 with .NET Core and Angular 2 project template.
I have multiple modules in my project that are currently served using same API endpoint. Please note, in the future I will have separate API endpoint for an individual module.
As the API endpoint is same for every module, the service proxy which is generated through NSwag will have all the modules' services.
I am having a requirement to have proxy generated based on an individual module, so for this, I thought of accomplishing using API versioning. So instead of v1, v2 etc., I will append the module name.
I have the following code:
Configuration.Modules.AbpAspNetCore()
.CreateControllersForAppServices(
typeof(Module).GetAssembly(),
"modulename"
);
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("module1", new Info { Title = "Module 1", Version = "module1" });
options.SwaggerDoc("module2", new Info { Title = "Module 2", Version = "module2" });
}
app.UseSwagger(options =>
{
options.RouteTemplate = "swagger/{documentName}/swagger.json";
});
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/module1/swagger.json", "Module 1");
options.SwaggerEndpoint("/swagger/module2/swagger.json", "Module 2");
});
This way, I am able to generate two endpoints as follows:
http://localhost:5000/swagger/module1/swagger.json
http://localhost:5000/swagger/module2/swagger.json
But both are having all the modules' information.
Please correct me or suggest a way to achieve the functionality.
It looks like you are using swashbuckle and not NSwag to generate your swagger docs. There are multiple ways to separate your docs by version, see swashbuckle documentation https://github.com/domaindrivendev/Swashbuckle.AspNetCore. The default method is to use your startup configs that you have above and decorate your methods with the corresponding ApiExplorer group name. The group name needs to match the first argument specified in the swaggerdoc config.
[ApiExplorerSettings(GroupName = "module1")] //Module 1 Method
You are also missing an 'e' in module for the second swagger endpoint.
see example: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/4bbf5cacd6ad0817b9015d699c559fd5c1cedf0d/test/WebSites/MultipleVersions/Startup.cs
To add multiple source edit the SwaggerUIOptions.
For example:
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("http://<ip1>:<port>/swagger/v1/swagger.json", "ServiceName1");
c.SwaggerEndpoint("http://<ip2>:<port>/swagger/v1/swagger.json", "ServiceName2");
});

SignalR 404 error when trying to negotiate

So I am very new to SignalR, in fact I've only been using it for a couple of days now. Anyway, I am getting the error below when my application first starts up:
The code for the application in question is located in two projects, a Web API and a Single Page Application (SPA). The first one has my backend code (C#) and the second one my client-side code (AngularJS). I think the problem might be due to the fact that the projects in question run on different ports. The Web API, where my SignalR hub lives, is on port 60161 and the SPA is on 60813. My hub is declared like so:
public class ReportHub : Hub
{
public void SendReportProgress(IList<ReportProgress> reportProgress)
{
this.Clients.All.broadcastReportProgress(reportProgress);
}
public override Task OnConnected()
{
this.Clients.All.newConnection();
return base.OnConnected();
}
}
and then in my Startup.cs file for my Web API I initialize SignalR like this:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.Services.Replace(typeof(IHttpControllerActivator), new NinjectFactory());
config.MessageHandlers.Add(new MessageHandler());
//set up OAuth and Cors
this.ConfigureOAuth(app);
config.EnableCors();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
// Setting up SignalR
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(new HubConfiguration { EnableJSONP = true });
});
//set up json formatters
FormatterConfig.RegisterFormatters(config.Formatters);
WebApiConfig.Register(config);
app.UseWebApi(config);
}
For my client-side code I use an Angular SignalR API called angular-signalr-hub (Angular-signalr-hub). The client-side follows:
angular
.module("mainApp")
.factory("reportHubService", ["$rootScope", "Hub", reportHubService]);
/// The factory function
function reportHubService($rootScope, Hub) {
var vm = this;
vm.reportName = "None";
// Setting up the SignalR hub
var hub = new Hub("reportHub", {
listeners: {
'newConnection': function(id) {
vm.reportName = "SignalR connected!";
$rootScope.$apply();
},
'broadcastReportProgress': function (reportProgress) {
vm.reportName = reportProgress.reportName;
$rootScope.$apply();
}
},
errorHandler: function(error) {
},
hubDisconnected: function () {
if (hub.connection.lastError) {
hub.connection.start();
}
},
transport: 'webSockets',
logging: true
//rootPath: 'http://localhost:60161/signalr'
});
I did some googling yesterday and one of the suggestions I came upon was to set the SignalR URL to the one of my Web API, which I did (the commented out line above). When I uncomment the line in question, that does seem to do something because if I now go to http://localhost:60161/signalr/hubs in my browser, it does show me the dynamically generated proxy file:
and when I run my application I no longer get the error above, but now it doesn't seem to connect. It gets to the negotiate line and it stops there:
I think it should look like this (this is from a SignalR tutorial I found):
In addition, none of my listeners (declared in my Angular code above) get called, so something is still now working quite right. There should be more lines in the log to the effect that connection was successfully established, etc. What could be the problem here?
UPDATE: upon further debugging i found out the problem is most likely being caused by the ProtocolVersion property being different between the client and the result here:
Because of that it seems it just exists and fails to establish connection.
I figured out what the problem was. My SignalR dependencies were out of date and because of that my client and server versions differed. All I had to do was update (via NuGet Package Manager) all SignalR dependencies to the latest version and now it works.
As a side note, SignalR was not very good at telling me what was wrong. In fact, no error message was displayed, unless of course there was some additional logging somewhere that had to be found or turned on, in addition to the logging I already had (turned on). Either way, it's either not logging certain errors or it makes it difficult to figure out how to turn on all logging. I had to go and debug the JQuery SignalR api to figure out what the problem was, which was a time consuming endeavour.

Web service error when invoked from web form

I have a program that utilize a web service. When this web service is tested on its own (run on ie AS .asmx) runs fine but when tried to be called from inside the web form it produces the following error:
Client found response content type of 'text/html; charset=utf-8', but expected 'text/xml'.
The web servce is:
public Check1 () {
//Uncomment the following line if using designed components
//InitializeComponent();
}
[WebMethod]
public void check2(string destination)
{
Server.Transfer(destination);
}
}
And the web form from which is called is:
protected void Button1_Click1(object sender, EventArgs e)
{
localhost.Check1 new2 = new localhost.Check1();
new2.check2("Ipal_apoth_page.aspx");
}
A web service is intended to be a data interface, not a web page server, which is why XML is expected. Web service protocols use XML format for their communications, e.g. WSDL or SOAP. The .aspx page you attempt to transfer the processing to will return HTML.
It may be that when you tried it in a browser, the browser was permissive enough to interpret the repsonse from the web service in the way you wanted even though that is not how it is meant to be used.
A better thing to practise with would be something simple like adding two numbers.

Maintain URL when going from ASP.NET WebForms to ASP.NET MVC

I am converting an old ASP.NET WebForms app to ASP.NET MVC 4. Everything is fine, except that I have a need to maintain backward compatibility with a specific URL. I found this great post on using UrlRewrite, but sadly that isn't something I can count on (this app gets deployed to lots of servers). At the bottom, he mentions using routing if you only have a small set of URLs to deal with, but doesn't provide any example.
Since I only have one url to deal with, I think routing would be the simple approach, but I've never dealt with anything except the default route /Controller/Action/{id} so I am looking for a solution that
Has no external dependencies
Will work on old crappy browsers
Doesn't matter if my app knows about this old url or not
The Old URI
https://www.mysite.com/default.aspx?parm1=p1&parm2=p2&etc=soforth
The New URI
https://www.mysite.com/Home/Index/?parm1=p1&parm2=p2&etc=soforth
Background: this app gets deployed to lots of servers at different locations. There are other apps (that I cannot update) that display the "Old URI" in a web-browser control, so I need them to continue to work after the app is updated to asp.net mvc.
Something like following should work (untested, may need to make this route to be one of the first):
routes.MapRoute(
"legacyDefaultPage",
"default.aspx",
new {Controller = "Legacy", Action="Default"});
class LegacyController {
ActionResult Default (string param1,...){}
}
Something that you could try is to create an httpModule this will get executed just before hit any route to in your MVC app.
In your http module you can perform a 301 or 302 redirection if seo matters doing that will give you more flexibility to transform all the parameters from your legacy to your new app.
public class RecirectionModule :IHttpModule{
public void Init(HttpApplication context)
{
_context = context;
context.BeginRequest += OnBeginRequest;
}
public void OnBeginRequest(object sender, EventArgs e)
{
string currentUrl = HttpContext.Current.Request.Url.AbsoluteUri;
string fileExtention = Path.GetExtension(currentUrl);
string[] fileList= new[]{".jpg",".css",".gif",".png",".js"};
if (fileList.Contains(fileExtention)) return;
currentUrl = DoAnyTranformation(currentUrl);
Redirect(currentUrl);
}
private void Redirect(string virtualPath)
{
if (string.IsNullOrEmpty(virtualPath)) return;
_context.Context.Response.Status = "301 Moved Permanently";
_context.Context.Response.StatusCode = 301;
_context.Context.Response.AppendHeader("Location", virtualPath);
_context.Context.Response.End();
}
public void Dispose()
{
}
}
I think that doing a redirection could be a cleaner solution if you need to change the parameter list in your new application also dealing with a lot of routes can get ugly pretty quickly.

Will this work when changing all the extensions in a site from .htm to .aspx?

I have been tasked with giving my company's website (~40 pages) a facelift. The original site is written in straight html/css/javascript and every file has the .htm extension. The new site is written in .net 3.5, Hosted on windows through iis.
I am not changing the directoy structure at all, but every page will go from a .htm extension to .aspx and I am concerned about how this will effect my SEO.
From another SO question I found a link to this article detailing a custom http module from which I have the following code:
public class PermanentRedirectHttpModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
HttpRequest request = context.Request;
if (request.Url.PathAndQuery.Contains(".htm"))
{
string url = request.Url.ToString();
url = url.Replace(".htm", ".aspx");
context.Response.Status="301 Moved Permanently";
context.Response.AddHeader("Location", url);
context.Response.End();
}
}
}
I have implemented this method and everything works as I would expect it to. Is this method acceptable and will it maintain my search engine rankings?
This would work by redirecting all of your .htm paths to .aspx. Because you're doing a 301 redirect, you might notice a temporary drop in search engine places as the power gets transferred. You'll also need to make sure that any links on your site go to the new URLs, otherwise you'll get alot of internal 301 redirects.
An alternative would be to use URL rewriting. This way you could maintain your .htm URLs, but they would be rewritten to point to the .aspx pages.
Helicon ISAPI (http://www.isapirewrite.com/) is one I often use. If you just have the 1 site on the server, you can get away with using the lite (free) version.
If you're using IIS7, you could use the built in rewrites which are configured in the web.config file of your site.

Categories

Resources