Dash-case on controllers routes with JsonApiDotNetCore - json-api

I'm trying to configure the JsonApiDotNetCore backend to communicate with my EmberJs frontend, but I have a problem with the naming convention for the controller. The backend is listening on http://localhost:3000/api/v1/articleCategories, but the frontend is requesting on http://localhost:3000/api/v1/article-categories. How can I change the routing on the backend side?

This is documented here.
From Startup.ConfigureServices:
services.AddJsonApi<AppDbContext>(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new KebabCaseNamingStrategy()
};
});

Related

.Net framework Owin Test Server does not work with WebSocket

My project is Owin self-hosted, it provides Web API endpoints and web socket endpoints.
Here is the relevant config code in the project's startup class
Owin WebSocket is used here
using Owin;
using Owin.WebSocket.Extensions;
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "api",
routeTemplate: "api/{version}/{controller}"
);
config.EnsureInitialized();
app.MapWebSocketRoute<WebSocket>("/api/v1/socket/test");
app.UseWebApi(config);
}
Works smoothly, when the app is launched I can consume the web api via "http://{host}/api/v1/test" and use the websockets by: "ws://{host}/api/v1/socket/test"
Then I decided to add some integration tests. I use Owin Test Server here. In TestServer.Create the config is identical:
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "api",
routeTemplate: "api/{version}/{controller}"
);
config.EnsureInitialized();
app.MapWebSocketRoute<WebSocket>("/api/v1/socket/test");
app.UseWebApi(config);
Test method for api
var url = new UriBuilder()
{
Scheme = "http",
Path = "/api/v1/test"
}.Uri;;
var result = client.GetAsync(url).Result;
Works nicely. But does not work for web socket:
var wsUri = new UriBuilder()
{
Scheme = "ws",
Path = "/api/v1/socket/test"
}.Uri;
//create websocket client, connect it and to server
var wsc = new ClientWebSocket();
Task.Run(async () =>
{
await wsc.ConnectAsync(wsUri, CancellationToken.None);
var a = wsc.State; // Here error thrown: No connection could be made because the target machine actively refused it 127.0.0.1:80
}).GetAwaiter().GetResult();
Why No connection could be made here? It seems like the testing server can only support regular http request not websocket. But this is not the case in the main app where the identical setting and framework is used. What am I missing here? I have been fiddling with this for hours to no avail...
Found the answer. The TestServer.Create<Startup>() only starts just the in-memory instance where the url is not available. The web socket client however relies on the url to work. So the solution is to user the overload WebApp.Start<Startup>(Settings.WebApiUrl) (it starts a web app on the url you provide)
WebApp.Start(new StartOptions("http://localhost:8989"), startup =>
{
startup.MapWebSocketRoute<TestConnection>();
startup.MapWebSocketRoute<TestConnection>("/ws", sResolver);
startup.MapWebSocketPattern<TestConnection>("/captures/(?<capture1>.+)/(?<capture2>.+)", sResolver);
});
Ref

IdentityServer4 - protecting WebAPI actions

I've successfully integrated my .net Core WebAPI with IdentityServer4 where a client is able to request a token and send back via auth. header when calling the controller and able to successfully authorize.
However, I do have a question around fined grained authorization.
When inserting a new ApiResource, I am also adding scopes (ex. app.api1.read, app.api2.read, etc..) and the client has access to the scopes (app.api1.read and app.api2.read). When the client calls app.api1, they can pass either scopes app.ap1.read or app.api2.read and both would be authorized successfully. Ideally, I would want the client to pass scope for api1 (ex. app.api1.read) and if they pass scope for api2 it should not be authorized.
Startup.cs
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = "https://localhost:44393";
options.RequireHttpsMetadata = false;
});
TestController.cs
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("api/[controller]")]
[ApiController]
public class TestController : Controller
{
Your assistance is much appreciated.

Access-Control-Allow-Origin error when using Owin

I'm have a aurelia client and a webserver. When i use localhost and i'm running on the same machine it works fine.
But when i want to access the server from another machine the page loads but the api calls give the following error:
No Access-Control-Allow-Origin header is present on the requested resource.
I'm using owin and to my undestanding i need to enable CORS for owin.
I did the follwing in my startup class:-
UPDATE
I have updated my class with input from Nenad but is still get the same error.
Below i have added the call from the client.
public void Configuration(IAppBuilder app)
{
this.container = new Container();
// Create the container as usual.
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
// Register your types, for instance using the scoped lifestyle:
container.Register<IWebDeps, WebDeps>(Lifestyle.Singleton);
// This is an extension method from the integration package.
container.RegisterWebApiControllers(GlobalConfiguration.Configuration, Assembly.GetExecutingAssembly());
container.Verify();
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// Configure Web API for self-host.
var config = new HttpConfiguration()
{
DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container)
};
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//// Custom Middleare
app.Use(typeof(CustomMiddleware));
app.UseWebApi(config);
//New code:
app.Run(context =>
{
context.Response.ContentType = "text/plain";
return context.Response.WriteAsync("Hello, world.");
});
}
My main program is calling the startUp class:-
using (Microsoft.Owin.Hosting.WebApp.Start<Startup>("http://localhost:8080"))
{
Console.WriteLine("Press [enter] to quit...");
Console.ReadLine();
}
Client code, 192.168.178.23 is the ip from the server.
let baseUrl2 = "http://192.168.178.23:8080/api/status/getStatus";
getStatus() {
return this.client.get(baseUrl2)
.then(response => {
return this.parseJSONToObject(response.content);
});
}
The error in Chrome:
XMLHttpRequest cannot load
http://192.168.178.23:8080/api/status/getStatus. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9000' is therefore not allowed
access. The response had HTTP status code 400.
Cors should be enabled now right? But i still get the error when doing a api call. Am i missing any steps? Our is this approah wrong?
Any suggestions are welcome!
You have to configure WebAPI to work with CORS.
Install Nuget package:
Install-Package Microsoft.AspNet.WebApi.Cors
Enable CORS on HttpConfiguration object:
config.EnableCors();
Add [EnableCors] attribute on your controller:
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
namespace WebService.Controllers
{
[EnableCors(origins: "www.example.com", headers: "*", methods: "*")]
public class TestController : ApiController
{
// Controller methods not shown...
}
}
or register it globally via HttpConfig:
var cors = new EnableCorsAttribute("www.example.com", "*", "*");
config.EnableCors(cors);
More details at: Enabling Cross-Origin Requests in ASP.NET Web API 2
Add this line and check,
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
and remove,
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
this worked for me.
Turns out that when starting OWIN the adres should be http://*:8080. Instead of local host.

Owin AuthenticationMode Active and Passive for different path

I'm using Owin WsFederation authentication. For unauthorized users I want one path to be redirected to STS and another to return 401 response. Is it possible to set different AuthenticationMode for different path?
You can "fork" the OWIN pipeline in order to configure middleware differently for different paths.
public void Configuration(IAppBuilder app)
{
app.UseErrorPage(new ErrorPageOptions());
app.Map("active", active =>
{
active.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active
//TODO: Add other options.
});
});
app.Map("passive", passive =>
{
passive.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Passive,
//TODO: Add other options.
});
});
}
This sample configures the "error page" middleware to run on all requests, then goes on to create two URL-mapped pipelines, one for requests which start /active and the other for paths which start /passive. Each of these mapped pipelines partially configure an OIDC authentication middleware, using the active and passive modes respectively.
This mechanism is designed to let you run different sets of middleware when requests come in on specific paths. Put common middleware before the mapped middleware to make it run on every request.
Not that I know of - but you can add more than one instance of the middleware in the pipeline, using different AuthenticationType, and use them on the different routes.

SignalR Negotiate 404

I am using SignalR 2.0. Everything works fine when running locally on my VS 2012. But when I publish the site on IIS, it breaks. The site loads but one of the scripts returns 404 Not Found. The script is something like.
https://example.com/signalr/negotiate?xxx
This path doesn't exist indeed. The correct path should be:
https://example.com/private/signalr/negotiate?xxx
Note the part in bold.
Inside the WebSite (https://example.com/) I have another Application (https://example.com/private/). This one is using SignalR.
This seems like a bug in SignalR since the signalr/hubs path is accessible from my private site.
I had a similar problem.
Here is the documentation for configuring the /signalr URL.
However, my solution differed from the docs.
Instead of changing the standard app.MapSignalR(), I changed my client code to use /MyApp/signalr. Here is the code where "MyApp" is the virtual directory of my web application.
var connection = $.hubConnection('/MyApp/signalr', {useDefaultPath: false});
var changesHub = connection.createHubProxy('changesHub');
changesHub.on('userCountChanged', function (count) {
$('#user-count').text(count);
});
connection.start().done(function () {
console.log('Hub has started');
changesHub.invoke('subscribeToChanges', user.id);
});
I tried the other way around (change the MapSignalR to the /signalr path) but this did not work and the negotiation was still routed to /MyApp/signalr/negotiate.
I had the same problem, with an application running in the IIS Default Web Site.
All the Microsoft examples show the hub url with a starting \, and I had copied those examples. But this meant that the signalr routing was from the Default Web Site rather than the application. Removing the leading \ solved it.
So I used endpoints in Startup.cs like:
endpoints.MapHub<MyHub>("myHub");
and hub connections in Javascript like:
var connection = new signalR.HubConnectionBuilder().withUrl("myHub").build();
I had the same issue when web site with signalr is not running as root site. Below solution worked for me. instead of using /signalr, use ../signalr. it will work with any site name folder. no hardcoded name 'MyApp'
var connection = $.hubConnection('../signalr', {useDefaultPath: false});
Had the same issue. web sites running as virtual directories of the root site. For some reason prefixing with ../ as in ../signalr didn't work, but ./signalr did.
My sample code:
function initSR() {
// logs signalr messages
$.connection.hub.logging = true;
// Declare a proxy to reference the hub.
var chat = $.connection.myHub;
$.connection.hub.url = "./signalr";
$.connection.hub.start();
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (message) {
// Process Message, take action upon receipt
alert(message);
};
}
I had the same problem, it is all about CORS, you should add Host URL in CORS config in Startup.cs like this:
services.AddCors(option =>
{
option.AddPolicy("AutomationCors", builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("YOUR LOCALHOST URL",
"YOUR HOST URL")
.AllowCredentials();
});
});
I faced the same problem. The mistake i was doing that i was calling the wrong endpoint url like i was mapping the Signal Url in Configure service like /notification but calling [API-Host]/api/notification. Removing the api from url and calling [API-Host]/notification fixed for me.
Probably you added MapSignalR() in your Application (https://example.com/private/).
If you want it on the root, then do the configuration on your WebSite (https://example.com/)
#styfle point me in the right direction the problem can be resolve in a more flexible way injecting BASE_URL (at least in angular 4)
import { Injectable, Inject } from '#angular/core';
import { HubConnection } from '#microsoft/signalr';
import * as signalR from '#microsoft/signalr';
import { Subject, Observable } from 'rxjs';
#Injectable()
export class SignalRService {
private hubConnection: HubConnection;
private message: Subject<any>;
constructor(#Inject('BASE_URL') private baseUrl: string) {
}
public connect() {
this.message = new Subject<any>();
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(this.baseUrl+"notification-hub")
.withAutomaticReconnect()
.build();
// ...
}
}

Resources