I have an ASP.NET Core Service that produces both JSON and XML responses. However, I like to restrict the accepted Media Type for only one action, so Swagger can only list application/json as a valid response content type. How can I achieve this in ASP.Net Core?
Please, consider I am using ASP.Net Core (ASP.NET MVC 6), not ASP.NET WebAPI.
UPDATE
Ok, so I'll add the answer as part of the same question. Thanks to @Helen, I was able to add the required classes to achieve this in ASP.Net Core (ASP.Net MVC 6). The answer is based on this answer but modified to use ASP.NET Core classes.
Step 1. Create a custom action filter attribute so the pipeline reacts to a forbiden content type:
/// <summary>
/// SwaggerResponseContentTypeAttribute
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class SwaggerResponseContentTypeAttribute : ActionFilterAttribute
{
/// <summary>
/// SwaggerResponseContentTypeAttribute
/// </summary>
/// <param name="responseType"></param>
public SwaggerResponseContentTypeAttribute(string responseType)
{
ResponseType = responseType;
}
/// <summary>
/// Response Content Type
/// </summary>
public string ResponseType { get; private set; }
/// <summary>
/// Remove all other Response Content Types
/// </summary>
public bool Exclusive { get; set; }
public override void OnActionExecuting(ActionExecutingContext context)
{
var accept = context.HttpContext.Request.Headers["accept"];
var accepted = accept.ToString().ToLower().Contains(ResponseType.ToLower());
if (!accepted)
context.Result = new StatusCodeResult((int)HttpStatusCode.NotAcceptable);
}
}
Step 2. Create a Swagger Operation Filter so the UI can reflect the restriction
public class ResponseContentTypeOperationFilter : IOperationFilter
{
public void Apply(Swashbuckle.AspNetCore.Swagger.Operation operation, OperationFilterContext context)
{
var requestAttributes = context.ControllerActionDescriptor.GetControllerAndActionAttributes(true).Where(c=>c.GetType().IsAssignableFrom(typeof(SwaggerResponseContentTypeAttribute))).Select(c=> c as SwaggerResponseContentTypeAttribute).FirstOrDefault();
if (requestAttributes != null)
{
if (requestAttributes.Exclusive)
operation.Produces.Clear();
operation.Produces.Add(requestAttributes.ResponseType);
}
}
}
Step 3. Configure Swagger UI service in Startup.cs, inside the method ConfigureServices, so it can use the newly created Operation Filter.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<MvcOptions>(options =>
{
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
});
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
c.OperationFilter<ResponseContentTypeOperationFilter>();
});
}
Step 4. Annotate the action
// GET api/values
[HttpGet]
[WebService.Utils.SwaggerResponseContentType(responseType: "application/json", Exclusive = true)]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
The [ApiController] attribute applies inference rules for the default data sources of action parameters. These rules save you from having to identify binding sources manually by applying attributes to the action parameters.
Controller derives from ControllerBase and adds support for views, so it's for handling web pages, not web API requests. There's an exception to this rule: if you plan to use the same controller for both views and web APIs, derive it from Controller.
OkObjectResult. An ObjectResult, when executed, performs content negotiation, formats the entity body, and will produce a Status200OK response if negotiation and formatting succeed. public OkObjectResult OkObjectResult() { return new OkObjectResult(new { Message="Hello World !"
This attribute produces more descriptive response details for web API help pages generated by tools like Swagger. [ProducesResponseType] indicates the known types and HTTP status codes to be returned by the action.
I know it is an old question but here I was trying to do that, so, in order to make it easier for other people I'll add to @joeystdio's answer with a "generic" way to add the same produce/consume attribute for all endpoints. (for MY use case, it's easier to setup things for all endpoints than going one by one.
.AddControllers(options =>
{
options.Filters.Add(new ProducesAttribute("application/json"));
options.Filters.Add(new ConsumesAttribute("application/json"));
// if you need a specific response type.
options.Filters.Add(new ProducesResponseTypeAttribute(typeof(ApplicationNotification), StatusCodes.Status500InternalServerError));
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With