I am trying to create a controller action that could respond to a webhook that sends application/xml. However I get 415 Unsupported Media Type error when trying to access it via Postman.
[PluginController("MyPlugin")]
public class MyPluginServiceController : UmbracoApiController
{ ...
[HttpPost]
[Consumes("application/xml")]
public IActionResult HandleXml([FromBody] XElement body)
{
return Content(body.ToString());
}
}
I've also configured services to AddXmlSerializerFormatters
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddXmlSerializerFormatters();
services
.AddUmbraco(_env, _config)
.AddBackOffice()
.AddWebsite()
.AddComposers()
.Build();
}
You should tell your controller to accept xml.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddXmlSerializerFormatters();
}
Related
I've got two Project in one solution:
WebApi
Blazor wasm
I start both projects with
In Program.cs is an external HttpClient added
builder.Services.AddHttpClient("WEbApi", client => client.BaseAddress = new Uri("http://localhost:12639"));
The Sender from my Index.razor.cs
using System.Net.Http;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Components;
public partial class Index
{
[Inject] private IHttpClientFactory ClientFactory { get; set; }
public void Send()
{
var client = ClientFactory.CreateClient("WEbApi");
var res = client.PostAsJsonAsync("order","FOO");
}
}
In the WEbApi Project the received controller code:
using Microsoft.AspNetCore.Mvc;
[Route("[controller]")]
public class OrderController : Controller
{
[HttpGet]
public IActionResult Index()
{
return Ok("GET done");
}
[HttpPost]
public IActionResult ExecuteOrder([FromBody] string order)
{
return Ok("POST done");
}
}
The GET request return OK.
The Problem:
client.PutAsJsonAsync("order","FOO") response a 405: method not allowed (listened via wireshark).
I try a POST with Postman, and it works!
I have to configure CORS in tzh WebApi project. Now it works.
Insert this code in the public void Configure() in the Startup.cs
app.UseCors(cors => cors
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true)
.AllowCredentials()
);
I need to access IApplicationBuilder inside a controller.
What I have tried :-
I have written middleware (app.UseMyMiddleware) as follows
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor httpContextAccessor)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMyMiddleware();
app.UseAuthentication();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
///TODO - Pass IApplicationBuilder to HttpContext
await _next(context);
}
}
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMiddleware>();
}
}
but I can't figure out how I can pass IApplicationBuilder to HttpContext in Invoke method. so, that I can use it in a controller.
I have also referred following stackoverflow question-answer
how to access IApplicationBuilder in a controller?
.Net Core Middleware - Getting Form Data from Request
Question(s) :-
How can pass IApplicationBuilder to HttpContext in Invoke method to use it in controller?
Is there any better way to access IApplicationBuilder inside controller apart from middleware?
IApplicationBuilder was not designed to work the way you want it to. Instead, if you have some data created at build time that you want to be available to middleware add a Singleton to the services and inject the singleton into the middleware.
You cannot access IApplicationBuilder anywhere later after completing the application building phase (after running Configure method). It's not available for injection at all.
However for the purpose of plugging-in or configuring middlewares at runtime based on request data (from HttpContext), you can use .UseWhen. Another one for terminal middleware is .MapWhen but I think that's not for your case. Here is an example of .UseWhen:
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
var allOptions = new [] {"option 1","option 2"};
foreach(var option in allOptions){
var currentOption = option;
builder.UseWhen(context => {
//suppose you can get the user's selected option from query string
var selectedOption = context.Request.Query["option_key"];
return selectedOption == currentOption;
}, app => {
//your MyMiddleware is supposed to accept one argument
app.UseMiddleware<MyMiddleware>(currentOption);
});
}
return builder;
}
}
To simplify it I suppose your options are just strings, you must know beforehand all possible options that the user can select via UI. Each one will be an exact match for the condition to plug-in a middleware and they must be all exclusive (so just one of them can enable one corresponding middleware), otherwise there will be duplicate middlewares, which may cause some issue.
By expressing the foreach above more clearly, it may represent something as follows:
//kind of pseudo code
if(selectedOption1){
app.UseMiddleware<MyMiddleware>("option 1");
} else if(selectedOption2){
app.UseMiddleware<MyMiddleware>("option 2");
}
...
You must decide how you get the selected option from the user (in the example above I get it from query string). You can get it from Cookie as well (to remember the user's selection) or from other sources such as route data, headers, form, request body. I think that's another issue, so if you have problem with that, please ask in another question.
First up all thanks to #Kingking and #GlennSills for there solution and valuable comments.
I have solved this problem as
Created one class which inherit from Hangfire.JobStorage as follows
public class HangfireSqlServerStorageExtension : Hangfire.JobStorage
{
private readonly HangfireSqlServerStorage _hangfireSqlServerStorage = new HangfireSqlServerStorage();
public HangfireSqlServerStorageExtension(string nameOrConnectionString)
{
_hangfireSqlServerStorage.SqlServerStorageOptions = new SqlServerStorageOptions();
_hangfireSqlServerStorage.SqlServerStorage = new SqlServerStorage(nameOrConnectionString, _hangfireSqlServerStorage.SqlServerStorageOptions);
}
public HangfireSqlServerStorageExtension(string nameOrConnectionString, SqlServerStorageOptions options)
{
_hangfireSqlServerStorage.SqlServerStorageOptions = options;
_hangfireSqlServerStorage.SqlServerStorage = new SqlServerStorage(nameOrConnectionString, _hangfireSqlServerStorage.SqlServerStorageOptions);
}
public void UpdateConnectionString(string nameOrConnectionString)
{
_hangfireSqlServerStorage.SqlServerStorage = new SqlServerStorage(nameOrConnectionString, _hangfireSqlServerStorage.SqlServerStorageOptions);
}
public override IStorageConnection GetConnection()
{
return _hangfireSqlServerStorage.SqlServerStorage.GetConnection();
}
public override IMonitoringApi GetMonitoringApi()
{
return _hangfireSqlServerStorage.SqlServerStorage.GetMonitoringApi();
}
}
HangfireSqlServerStorage.cs
Used in HangfireSqlServerStorageExtension class above
public class HangfireSqlServerStorage
{
public SqlServerStorage SqlServerStorage { get; set; }
public SqlServerStorageOptions SqlServerStorageOptions { get; set; }
}
Startup.cs
In Startup file add singleton service for HangfireSqlServerStorageExtension instance and configure hangfire dashboard as follows
public class Startup
{
///Other necessary code here
public static HangfireSqlServerStorageExtension HangfireSqlServerStorageExtension { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
///Other necessary code here
HangfireSqlServerStorageExtension = new HangfireSqlServerStorageExtension("DBConnecttionString"));
services.AddSingleton<HangfireSqlServerStorageExtension>(HangfireSqlServerStorageExtension);
services.AddHangfire(configuration => configuration.SetDataCompatibilityLevel(CompatibilityLevel.Version_170));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor httpContextAccessor)
{
//Other necessary code here
app.UseHangfireDashboard("/Dashboard", new DashboardOptions(), HangfireSqlServerStorageExtension);
//Other necessary code here
}
}
Inside controller I have used it as follows
HangfireController.cs
public class HangfireController : Controller
{
protected readonly HangfireSqlServerStorageExtension
hangfireSqlServerStorageExtension;
public HangfireController(HangfireSqlServerStorageExtension hangfireSqlServerStorageExtension)
{
this.hangfireSqlServerStorageExtension = hangfireSqlServerStorageExtension;
}
public IActionResult DisplayHangfireDashboard()
{
// Update connString as follows
hangfireSqlServerStorageExtension.UpdateConnectionString(connString);
var hangfireDashboardUrl = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}" + "/Dashboard";
return Json(new { url = hangfireDashboardUrl });
}
}
I'd like to have a console application running a standalone webserver accepting REST calls. My application is a .NET Core app with ASP .NET Core inside. I am completely new in this area. I found some examples and now I am struggling with controller route configuration. With the code below I always get "404 Not Found" error when using http://localhost:3354/api/Demo/Hello. Does anyone have an idea what am I doing wrong? Thank you for any suggestion!
I use VS2019 and ASPNETCORE 2.2.8.
class Program
{
static void Main(string[] args)
{
var builder = WebHost.CreateDefaultBuilder()
.ConfigureKestrel(options => options.ListenAnyIP(3354))
.UseStartup<Startup>();
builder.Build().Run();
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder builder, IHostingEnvironment env)
{
builder.UseMvc(delegate(IRouteBuilder routeBuilder)
{
routeBuilder.MapRoute("default", "api/{controller}/{action}");
});
}
}
Here comes the DemoController class.
public class DemoController : Controller
{
public IActionResult Hello()
{
return Ok("Hello world");
}
}
Your example is working fine for me on .net core 2.2
You could try explicitly declare routes like
[ApiController]
[Route("api/[controller]")]
public class DemoController : Controller
{
[HttpGet("hello")]
public IActionResult Hello()
{
return Ok("Hello world");
}
}
Also you could consider using Visual studio built-in templates of api web applications
After some investigation and comparison of my project with the sample project of Roman Kalinchuk I found out that the problem is that mvc controller provider doesn't look for controller types in my application assembly. It is enought to add my application assembly to the application parts collection.
See the .AddApplicationPart(typeof(DemoController).Assembly); line.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddApplicationPart(typeof(DemoController).Assembly);
}
public void Configure(IApplicationBuilder builder, IHostingEnvironment env)
{
env.EnvironmentName = "Development";
builder.UseMvc(delegate(IRouteBuilder routeBuilder)
{
routeBuilder.MapRoute("test", "api/{controller}/{action}");
});
}
}
When debugging in IIS Express all endpoints are reachable via GET. When published to IIS10 I can navigate to the page public void OnGet() is being called and renders the razor page. When calling ./MyPage/Partial on server IIS10 I receive a 404 Not Found error and this does not happen on IIS Express in Visual Studio.
public class IndexModel : PageModel
{
[BindProperty]
public MyModel MyModel { get; set; }
[HttpGet]
public void OnGet()
{
...
}
[HttpGet]
public IActionResult OnGetPartial([FromQuery] int id)
{
...
}
}
I have followed the instructions on https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/?view=aspnetcore-2.2 and my best guess is that I need to configure these routes as per https://learn.microsoft.com/en-us/aspnet/core/razor-pages/razor-pages-conventions?view=aspnetcore-2.2
Although my question is why in IIS Express I can call javascript jquery $.load('./MyPage/Partial?id=1') and it works fine and when published it returns a 404 error? And what would be the specific solution?
EDIT: in my Index.cshtml I have the following #page "{handler?}" at the top in order to handle the custom REST methods.
In order to solve this I followed the instructions from https://learn.microsoft.com/en-us/aspnet/core/razor-pages/razor-pages-conventions?view=aspnetcore-2.2 in the file Startup.cs or whichever class you are using in Program.cs via
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseStartup<Startup>();
In the method in the file Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().AddRazorPages(options => options.Conventions.AddPageRoute("/MyPage", "/MyPage/Partial/{id}")).AddRazorViewEngine().AddViews();
// Other service code impl. here
}
Hello i am trying to issue a http get request to a .NET Core Console App from my Angular 2 frontend and i get the following error:
Access to XMLHttpRequest at 'http://127.0.0.1:9300/api/getusers' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
For me it is curious since i have enabled CORS on the server side as you can see below in the Startup class.
Startup
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services) {
services.AddOptions();
services.AddMvc();
}
public IConfiguration Configuration;
public void Configure(IApplicationBuilder app) {
Console.WriteLine("request delievered");
Debug.WriteLine("Entered Server !");
app.UseMvc();
app.UseCors(x => { x.AllowAnyHeader(); x.AllowAnyOrigin();x.AllowAnyMethod(); });
}
}
I make the request from the UI like this:
#Injectable()
export class UserService{
private static baseUrl:string="http://127.0.0.1:9300/api";
constructor(private http:HttpClient) {
}
getClientsAsync():Promise<User[]>{
let route=UserService.baseUrl+"/getusers";
var data=(this.http.get(route) //should i have some headers here?
.map(resp=>resp)
.catch(err=>
Observable.throwError(err)
) as Observable<User[]>).toPromise<User[]>();
return data;
}
}
P.S I have tried with Postman and the request works ,however here in the angular 2 i have not included any headers for my http.get method.Could this be the problem ?
You need to put UseCors before UseMvc.
public void Configure(IApplicationBuilder app) {
Console.WriteLine("request delievered");
Debug.WriteLine("Entered Server !");
app.UseCors(x => { x.AllowAnyHeader(); x.AllowAnyOrigin();x.AllowAnyMethod(); });
app.UseMvc();
}
This is because UseCors adds a middleware (as does UseMvc), and middleware are executed in order from top to bottom. So the request never gets to the CORS middleware.