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" };
}
You can use the annotations Consumes and Produces. These are also picked up by Swashbuckle. Like so:
[HttpGet]
[Consumes("application/xml")]
[Produces("application/xml")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}