Controller Configuration in AspNetCore - asp.net-core

I would like to configure one (and only one) of my Controller to accept only application/xml requests.
In the past i used IControllerConfiguration to do that like described here (Per-Controller configuration).
How can i do that in Aspnet Core ?

You can use the Consumes-Annotation together with the accepted content type on Controller or Action level.
With
[Consumes("application/xml")]
public class MyController : Controller
{
public IActionResult MyAction([FromBody] CallModel model)
{
....
}
}
calls to this controller will only succeed if the client provides Content-Type header of application/xml. Otherwise a 415 (Unsupported Media Type) will be returned.

You may simply check Request AcceptTypes / Content-Type headers (like if request.AcceptTypes.Contains("application/xml")) and stop request processing.
Accordingly to link you provided, seems like you just want to ignore content type and always return an XML result. In this case you may use a new Produces attribute.
A filter that specifies the expected System.Type the action will return and the supported response content types. The Microsoft.AspNetCore.Mvc.ProducesAttribute.ContentTypes value is used to set Microsoft.AspNetCore.Mvc.ObjectResult.ContentTypes.
Apply attribute to your controller
[Produces("application/xml")]
public YourXmlController : Controller { }
or only to specific controller action:
[Produces("application/xml")]
public Object ControllerAction()
{
return new { text = "hello world" };
}
Note, that XML formatter does not enabled by default, so you should add one using MvcOptions:
services.Configure<MvcOptions>(options =>
{
//options.InputFormatters.Add( ... );
//options.OutputFormatters.Add( ... );
});

Related

How to return a status code from an endpoint that can then be handled by app.UseStatusCodePages() middleware?

If I return StatusCode(403) or any other error code from an endpoint, any configuration of app.UseStatusCodePages<whatever> will be ignored.
I believe this is because the StatusCode(<whatever>) will automatically create a result object, and UseStatusCodePages only kicks in if there is an error status code and no content.
So how do I set a status code result in an IActionResult type endpoint and then return without setting any content so that UseStatusCodePages will handle the job of providing a suitable resonse?
As far as I know, the UseStatusCodePages will just be fired when the action result is the StatusCodeResult.
If you put some value inside the status codes, it will return the object result which will not trigger the UseStatusCodePages.
So I suggest you could directly use StatusCodeResult(403), then if you want to put some value to the StatusCodeResult, I suggest you could put it inside the httpcontext's item.
More details, you could refer to below codes:
public IActionResult OnGet()
{
HttpContext.Items.Add("test","1");
return StatusCode(403);
}
Program.cs:
app.UseStatusCodePages(async statusCodeContext =>
{
var status = statusCodeContext.HttpContext.Items["test"];
// using static System.Net.Mime.MediaTypeNames;
statusCodeContext.HttpContext.Response.ContentType = Text.Plain;
await statusCodeContext.HttpContext.Response.WriteAsync(
$"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});
Result:
The issue was that I have the ApiController attribute on the endpoint controller. One of the things this attribute does is to automatically create a ProblemDetails response body for any failed requests, and it is this that prevents UseStatusCodePages from having any effect.
The solution is to either remove the ApiController attribute if you do not require any of its features, or alternatively its behaviour of automatically creating ProblemDetails responses can be disabled using the following configuration in Program.cs (or Startup.cs in old style projects).
builder.Services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
options.SuppressMapClientErrors = true;
});

ProducesAttribute causes "No output formatter was found for content types"

Consider this simple controller action:
[HttpGet("{imageId}")]
[ResponseCache(Duration = 604800)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Produces("image/jpeg")]
public async Task<IActionResult> GetImageAsync(int imageId)
{
if (imageId <= 0)
{
return NotFound();
}
byte[] imageBytes = await _imageProvider.GetImageAsync(
imageId,
Request.HttpContext.RequestAborted).ConfigureAwait(false);
if (imageBytes is null)
{
return NotFound();
}
return File(imageBytes, MediaTypeNames.Image.Jpeg);
}
This method works fine, however in telemetry I am getting this for every call:
Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor: Warning: No output formatter was found for content types 'image/jpeg, image/jpeg' to write the response.
Simply commenting out the ProducesAttribute prevents the telemetry from being logged.
I want to keep ProducesAttribute because it enables my Swagger UI page to work. In Swagger it shows this API has an expected output media type of image/jpeg. If I remove the attribute it changes to text/plain and it doesn't render correctly.
So the question is, how can I fix this controller action to not create a bunch of unnecessary telemetry, while also allowing Swagger UI to work correctly?

Route to allow a parameter from both query string and default {id} template

I have an action in my ASP.Net Core WebAPI Controller which takes one parameter. I'm trying to configure it to be able to call it in following forms:
api/{controller}/{action}/{id}
api/{controller}/{action}?id={id}
I can't seem to get the routing right, as I can only make one form to be recognized. The (simplified) action signature looks like this: public ActionResult<string> Get(Guid id). These are the routes I've tried:
[HttpGet("Get")] -- mapped to api/MyController/Get?id=...
[HttpGet("Get/{id}")] -- mapped to api/MyController/Get/...
both of them -- mapped to api/MyController/Get/...
How can I configure my action to be called using both URL forms?
if you want to use route templates
you can provide one in Startup.cs Configure Method Like This:
app.UseMvc(o =>
{
o.MapRoute("main", "{controller}/{action}/{id?}");
});
now you can use both of request addresses.
If you want to use the attribute routing you can use the same way:
[HttpGet("Get/{id?}")]
public async ValueTask<IActionResult> Get(
Guid id)
{
return Ok(id);
}
Make the parameter optional
[Route("api/MyController")]
public class MyController: Controller {
//GET api/MyController/Get
//GET api/MyController/Get/{285A477F-22A7-4691-AA51-08247FB93F7E}
//GET api/MyController/Get?id={285A477F-22A7-4691-AA51-08247FB93F7E}
[HttpGet("Get/{id:guid?}"
public ActionResult<string> Get(Guid? id) {
if(id == null)
return BadRequest();
//...
}
}
This however means that you would need to do some validation of the parameter in the action to account for the fact that it can be passed in as null because of the action being able to accept api/MyController/Get on its own.
Reference Routing to controller actions in ASP.NET Core

ServiceStack authentication with both [Authenticate] and [ValidateApiKey] attributes

I have some endpoints decorated with the [Authenticate] attribute. Now a third party client has to access the same endpoint by using a shared API key.
As the code would be exactly the same for the two cases, I would like to check first if the current request comes from an authenticated user and, if not, checks as fallback if a valid API key is provided.
Is there a way to use both [Authenticate] and [ValidateApiKey] attributes for the same endpoint?
Something like:
[Authenticate | ValidateApiKey]
public long Post(MyDto request)
{
// ....
}
Attributes can only be combined to add functionality, i.e. they can't be used as a fallback or a switch. To get the desired behavior your [ValidateApiKey] attribute should perform the validation fallback as part of its implementation, e.g:
public class ValidateApiKeyAttribute : RequestFilterAttribute
{
public override void Execute(IRequest req, IResponse res, object reqDto)
{
var session = req.GetSession();
if (session == null || !session.IsAuthenticated)
{
//If not a valid key, execute the `[Authenticate]` attribute
//to handle failed response
if (!CheckValidApiKey(req))
new AuthenticateAttribute().Execute(req,res,reqDto);
}
}
}
Note: Responses should be reference types (e.g. DTO's) or raw strings not value types.
public object Post(MyDto request)
{
// ....
}

Request and Response Headers Override using Restler

I am new to restler and trying to do the following things, can't seem to get hold of it
I have this class and method exposed via Restler
class Account {
protected $Api_Version = array('version' => "1.0.2.1234", 'href' => "/");
// Returns the version of the service
// Content-Type: application/vnd.cust.version+json
function version() {
return json_encode($this->version);
}
// Accepts only Content Type: application/vnd.cust.account+json
function postCreate() {
}
}
1) I want to return my own Content-Type to client like in the 'version' method instead of default application/json. In my case its 'application/vnd.cust.version+json'
2) Method postCreate should only accept the request if the Contet-Type is set to 'application/vnd.cust.account+json'. How to check if that header is set in the request.
3) Also in the restler API Explorer, for methond name, how can I show only the method name instead of the 'version.json'. I want to show just 'version' like the method name
Thank you for your help.
Narsi
1) maybe Write your own format? Take a Look at
http://restler3.luracast.com/examples/_003_multiformat/readme.html
2) you could check the headers and throw Exception on wrong one.
Take a Look at this link
http://stackoverflow.com/questions/541430/how-do-i-read-any-request-header-in-php
3) have you tried to and the following line to your Index.php?
Resources::$useFormatAsExtension = false
Hope takes you further on :)
CU
Inge