ASP.NET Core 1.0 WebSocket Setup? - websocket

I'm struggling to find an example to setup WebSockets in ASP.NET Core 1.0; they all seem to be for the previous versions of ASP.NET and some rely on properties that don't seem to exist under context (for me).
Main documentation only has a placeholder too. http://docs.asp.net/en/latest/
For example:
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.IsWebSocketRequest)
{
WebSocket webSocket = await context.AcceptWebSocketAsync();
await EchoWebSocket(webSocket);
}
else
{
await next();
}
});
Doesn't work because IsWebSocketRequest doesn't exist now. What is the correct approach in ASP.NET Core 1.0?

After some disassembly it looks like its been moved around a little; and there is a new WebSocketManager
app.UseWebSockets();
app.Use(async (context, next) =>
{
var http = (HttpContext) context;
if (http.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await http.WebSockets.AcceptWebSocketAsync();
}
});
Also turns out that because there was a compile error, it assumed context was of type RequestDelegate. After fixing the usage to context.WebSockets.IsWebSocketRequest it now knows that context is HttpContext

Related

CORS issue with an ASP.NET Core Web API using app.UseEndpoints

I create a new project Microsoft Visual 2022 with the template ASP.NET Core 6.0 Web API with use of controllers.
The endpoint is https://localhost:7251/weatherforecast.
I set my program.cs:
string? origins = "_myAllowSpecificOrigins";
builder.Services.AddCors(options =>
{
options.AddPolicy(origins, builder => builder.WithOrigins("https://localhost:7041")
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
app.UseCors(origins);
Then I create a new project Microsoft Visual 2022 with the template Blazor Web Assembly App NET 6.0 without ASP.NET Core Hosted.
I write this basic call:
var client = new HttpClient();
var response = await client.GetFromJsonAsync<WeatherForecast[]>("https://localhost:7251/weatherforecast");
...and it works.
Now, for some project requirements, I have to replace in my API.program.cs
app.MapControllers();
with
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
Then I get an error
fetch is blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
For info, if the web app is a Client/Server/Shared, there is no problem.
How can I set the communication web app not hosted/API using endpoints?
It turns out I just had to add in the Program.cs of the API
builder.Services.AddControllers();
Not using the default client/server template in my web app makes me skip some behaviors and requires this line.
Maybe it's could be related your response headers. Can you try add this;
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
context.Response.Headers["Access-Control-Allow-Origin"] = "*";
context.Response.Headers["Access-Control-Allow-Credentials"] = "true";
return Task.CompletedTask;
});
await next();
});

In Webpack's dev server, how can I use a fallback on proxy error?

I have a Vue.JS project where a customization css is served by the backend. This can be configured using devServer.proxy.
When the backend doesn't have a custom css to serve (reutrns 404), is there a way to fallback to serving a static file ?
I tried:
devServer: {
proxy: {
'^/custom.css$': {
target: backend,
onError(err, req, res, target) { res.end(static_css) /* never called */ }
}
}
}
But onError is not called, so I may have misunderstood the doc but don't see any way to implement a fallback. Is is at all possible ?
I know I can implement this at some other level (e.g. in the browser or the backend), but this question is specifically about proxy fallback in webpack dev server.
Apparently there is a before option which allows to install our own handlers. It's not documentend but I'm not using the latest webpack. I guess it's setupMiddlewares now.
The before works like so for my specific need, but it's very flexible:
const http = require('http');
/* ... */
devServer: {
before(app) {
app.use(function(req, res, next) {
if(req.url == '/custom.css') {
http.get(backend + '/custom.css', upstream => {
if(upstream.statusCode != 200) next();
else upstream.pipe(res);
}).on('error', next).on('timeout', next);
} else next();
})
}
}
The newer setupMiddlewares is documented here.
Apparently there's also onBeforeSetupMiddleware which is deprecated on latest but unavailable on my version so I guess it's usable for versions in between.
I didn't find a lot of ressources on advanced webpack dev server use-cases other than looking at the code, so I hope this helps.

Is there a way to tell if your app is running in Microsoft Teams

I have a web app that needs to execute specific code if it is running in Microsoft Teams, however I haven't yet figured out if there is any way to tell if your app is running in teams. Any ideas or insight?
edit:
for anyone wondering we ended up using a combination of the two answers below, on app start it will check the url of the app to see if it contains "/teams". The teams app is told specifically to point to {app_name}/teams, if this case is true it will run the following code block:
import * as microsoftTeams from '#microsoft/teams-js';
if (window.location.pathname.includes('teams')) {
microsoftTeams.initialize(() => {
microsoftTeams.getContext(context => {
store.dispatch(teamsDetected(context.hostClientType!));
try {
microsoftTeams.settings.registerOnSaveHandler(saveEvent => {
microsoftTeams.settings.setSettings({
websiteUrl: window.location.href,
contentUrl: `${window.location.href}&teams`,
entityId: context.entityId,
suggestedDisplayName: document.title
});
saveEvent.notifySuccess();
});
microsoftTeams.settings.setValidityState(true);
} catch (err) {
console.error('failed to set teams settings')
}
});
});
}
As you have probably experienced, a call to microsoftTeams.getContext(...) never returns if you are not in Teams.
So I have a flag that I monitor with a setInterval and if this._teamsContext is truthy, and has sane values; and only if if has this._hasAttemptedConnection
It is a bit of a round-a-bout way.
Another mechanism I implemented a little later was passing in a flag with the URL entrypoint (in our case: this is a Teams Tab) https://<oururl>?context=teams and only using the Teams codepath when in Teams.
I have seen requests in the Microsoft Teams .js github to return a failure from the microsoftTeams.getContext(...) refer: is there any API to detect running in Teams or not?
Prior to the flag, I had some Typescript code that looks like
WireTeams(): Promise<boolean> {
this._hasAttemptedConnection = false
return new Promise<boolean>((resolve, reject) => {
microsoftTeams.initialize()
microsoftTeams.getContext((context) => {
if (context === null || context === undefined) {
resolve(false)
}
this._teamsContext = context
})
})
this._hasAttemptedConnection = true
}
As of 2022, Microsoft released version 2.0 of teams-js library. You can check https://www.npmjs.com/package/#microsoft/teams-js. You can now use the app module to check whether it is initialized.
import { app } from '#microsoft/teams-js';
bool initialized = app.isInitialized()

Web Api 2 all errors logging

Some errors get uncaptured (like ModelBinding exceptions or route change failure exceptions) when I use ExceptionLogger and ExceptionHandler implementations.
The question is how to log all exceptions fired in Web Api 2 app?
Btw, I use OWIN to host it.
What about a catch-all middleware?
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
// log
}
});

Cross origin SignalR connection stops after negotiate

I have an MVC 5 app serving up views, and a Web API 2 app as the service layer (.NET 4.5). The Web API app uses SignalR 2.1.2 to return progress as it's processing POSTs to the service API. The two are deployed to different domains, so I've set up cross origin support as per the asp.net tutorial article.
[assembly: OwinStartup(typeof (Startup))]
namespace MyApp.Service
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
//worry about locking it down to specific origin later
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(new HubConfiguration());
});
//now start the WebAPI app
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
WebApiConfig.cs also contains its own CORS declaration.
namespace MyApp.Service
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//controller invocations will come from the MVC project which is deployed to a
//different domain, so must enable cross origin resource sharing
config.EnableCors();
// Web API routes
config.MapHttpAttributeRoutes();
//Snip other controller dependency initialisation
}
}
}
I've defined a simple hub class with no server-side API (it's only to allow the server to push to the clients, not for the clients to call into).
namespace MyApp.Service.Hubs
{
[HubName("testresult")]
public class TestResultHub : Hub
{
}
}
Since I'm going cross-domain AND the hub is not exposing any server side API, I'm not bothering to use a generated JS proxy.
The relevant bits of the JS that set up the signalr hub connection is: (remember this is being served up from the MVC app, which does not have any signalr support (except jquery-signalr-{version}.js of course))
function TestScenarioHandler(signalrHubUrl) {
var self = this;
//Snip irrelevant bits (mostly Knockout initialisation)
self.signalrConnectionId = ko.observable();
var hubConnection = $.hubConnection(signalrHubUrl, { useDefaultPath: false });
var hubProxy = hubConnection.createHubProxy("testresult");
hubProxy.on("progress", function(value) {
console.log("Hooray! Got a new value from the server: " + value);
});
hubConnection.start()
.done(function() {
self.signalrConnectionId(hubConnection.id);
console.log("Connected to signalr hub with connection id " + hubConnection.id);
})
.fail(function() {
console.log("Failed to connect to signalr hub at " + hubConnection.url);
});
}
Going cross-origin like this, Firefox network traffic shows (and I've confirmed Chrome shows the same thing) a GET to
http://****service.azurewebsites.net/signalr/negotiate?clientProtocol=1.5&connectionData=[{"name":"testresult"}]&_=1424419288550
Notice that the name matches the value of the HubName attribute on my hub class.
This GET returns HTTP 200, the response gives me a JSON payload containing a ConnectionId, ConnectionToken, and a bunch of other fields that suggests everything's ok. The HTTP response also has the Access-Control-Allow-Origin: header set to the domain that the GET originated from. All up it looks good, except that's where the traffic stops.
But the JS console prints "Failed to connect to signalr hub at http://****service.azurewebsites.net/signalr"
To verify I'm not doing anything too stupid, I've added signalr support and a basic hub to the MVC app (so no cross origin required), and changed the $.hubConnection() and hubConnection.createProxy() calls accordingly. When I do that, browser traffic shows the same /signalr/negotiate?... GET (obviously not cross origin any more), but then also GETs to /signalr/connect?... and /signalr/start?.... The JS console also prints a success message.
So in summary;
CORS is enabled on the service layer, and the signalr /negotiate GET returns 200, what appears to be a valid connection id, and the expected Access-Control-Allow-Origin: header. This suggests to me that the server-side CORS support is behaving itself correctly, but the signalr connection does not succeed.
When I reconfigure so the signalr connection is NOT cross origin, everything works as expected.
WTF am I missing or doing wrong?! Some conflict between HttpConfiguration.EnableCors() and IAppBuilder.UseCors(CorsOption) perhaps?
Solved it. I had changed the map.UseCors(CorsOptions.AllowAll) to pass in a CorsPolicy object instead, and set SupportsCredentials to false, having read elsewhere that Access-Control-Allow-Origin: * is incompatible with access-control-allow-credentials: true.
private static readonly Lazy<CorsOptions> SignalrCorsOptions = new Lazy<CorsOptions>(() =>
{
return new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context =>
{
var policy = new CorsPolicy();
policy.AllowAnyOrigin = true;
policy.AllowAnyMethod = true;
policy.AllowAnyHeader = true;
policy.SupportsCredentials = false;
return Task.FromResult(policy);
}
}
};
});
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(SignalrCorsOptions.Value);
map.RunSignalR(new HubConfiguration());
});
//now start the WebAPI app
GlobalConfiguration.Configure(WebApiConfig.Register);
}
Setting SupportCredentials to true results in the Access-Control-Allow-Origin header being rewritten with the actual origin (not *) and access-control-allow-credentials: true in the response.
And now it works.
For me following settings did good job
services.AddCors(c =>
{
c.AddPolicy("AllowCCORSOrigin", options => options
.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
});

Resources