I'm using Swashbuckle to generate docs for an API. My controller methods looks like this:
[ResponseType(typeof(CategoryCollectionModel))]
public HttpResponseMessage Get(HttpRequestMessage request, [FromUri]Paging paging)
{
var input = new CategoriesListQuery.Input { Page = paging.Page, Size = paging.Size };
var result = this.queryInvoker.Execute<CategoriesListQuery.Input, CategoriesListQuery.Result>(input);
var items = Mapper.Map<CategoryCollectionModel>(result);
return request.CreateResponse(HttpStatusCode.OK, items);
}
Swashbuckle treats HttpRequestMessage as a parameter in the generated docs. Is there a way to configure Swashbuckle to ignore HttpRequestMessage since it is only included in the signature for testing purposes?
Please refer to the discussion here. In short do not pass in HttpRequestMessage as in input parameter, rather mock the {controller}.Request property.
I found a solution from "http://www.morganskinner.com/2016/02/ignoring-parameters-in-swashbuckle.html"
Summary :
In Swashbuckle you can plug-in operation “filters” that can be used to
alter the emitted data – the filter is passed the context of the
operation being emitted, and you can monkey around with the data that
pops out. All I had to do then was create a filter that would look for
this datatype, and remove the corresponding data from the results. I
ended up with this…
public class IgnoreHttpRequestMessageOperationFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry,
ApiDescription apiDescription)
{
apiDescription.ParameterDescriptions
.Where(desc => desc.ParameterDescriptor.ParameterType
== typeof(HttpRequestMessage))
.ToList()
.ForEach(param =>
{
var toRemove = operation.parameters
.SingleOrDefault(p => p.name == param.Name);
if (null != toRemove)
operation.parameters.Remove(toRemove);
});
}
}
With that class in place, I just needed to plug this in to the swagger
config file as follows...
c.OperationFilter<IgnoreHttpRequestMessageOperationFilter>();
Working for me. thanks "Morgan"
Related
How can I set swagger to shown all possible responses?
Now only shows the HTTP 200, but there are more possible responses
I have a global exception handling class and I would like to a global solution.
Nothing, but now i will try some possible solutions.
If i use it:
[ProducesResponseType(200)]
[ProducesResponseType(401)]
[ProducesResponseType(404)]
at controller method it is work and swagger shows more possible responses. But i would like to a global solution
Is there a swagger settings or something else?
If you add the following attributes to the methods you can specify the output model and the error
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(YOUROBJECT))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
So https://stackoverflow.com/users/113116/helen find the correct and beauty solution.
Swashbuckle general response codes
Is a way to declare common response types to different actions?
What i used for general response handling:
/in AddSwaggerGen
options.OperationFilter<GeneralExceptionOperationFilter>();
.
internal class GeneralExceptionOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
operation.Responses.Add("401", new OpenApiResponse() { Description = "Unauthorized" });
operation.Responses.Add("403", new OpenApiResponse() { Description = "Forbidden" });
//Example where we filter on specific HttpMethod and define the return model
var method = context.MethodInfo.GetCustomAttributes(true)
.OfType<HttpMethodAttribute>()
.Single();
if (method is HttpDeleteAttribute || method is HttpPostAttribute || method is HttpPatchAttribute || method is HttpPutAttribute)
{
operation.Responses.Add("409", new OpenApiResponse()
{
Description = "Conflict",
Content = new Dictionary<string, OpenApiMediaType>()
{
["application/json"] = new OpenApiMediaType
{
Schema = context.SchemaGenerator.GenerateSchema(typeof(string), context.SchemaRepository)
}
}
});
}
}
}
I need to select OutputFormatter depending on the query parameter. How to do that?
I am moving from .NET Framework WebApi to .NET Core WebApi. The .NET Framework WebApi had DefaultContentNegotiator class do that:
public class CustomContentNegotiator : DefaultContentNegotiator
{
public override ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
//Read query from request object and add output formatters below
bindFormatters = new List<MediaTypeFormatter>
{
new ConvertResultRawFormatter(),
new JsonMediaTypeFormatter
{
SerializerSettings =
{
NullValueHandling = NullValueHandling.Ignore
}
}
};
}
return base.Negotiate(type, request, bindFormatters);
}
}
replace in configuration with new formatted negotiator
config.Services.Replace(typeof(IContentNegotiator), new CustomContentNegotiator());
Will the custom output formatters not work for you? Read this ms docs
From the comments it seems that the real question is how to add the Content-Disposition: inline header if the URL contains download=inline parameter. This doesn't concern formatting or content negotiation.
There are several ways to add headers to a response. One of them is to add inline middleware that adds the header if the query parameter is present :
app.Use(async (context, next) =>
{
context.Response.OnStarting(() =>
{
var download= context.Request.Query["download"];
if (download=="inline")
{
context.Response.Headers.Add("Content-Disposition", "inline");
}
}
}
This affects all routes
Premise
The documented method to apply resource-based authorization in ASP.Net Core is to register an AuthorizationHandler, define each OperationAuthorizationRequirement, then check access to resources using the AuthorizeAsync() method of an injected IAuthorizationHandler. (Reference docs)
This is all well and good for checking operations against individual records, but my question is how best to authorize against many resources at once (e.g. checking read permission against a list of records for an index page)?
Example
Let's say we have a list of orders, and we want to provide users with a list of the ones they have created. To do this with the practice defined by Microsoft's docs, we would first create some static OperationAuthorizationRequirement objects:
public static class CrudOperations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = nameof(Create) };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = nameof(Read) };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = nameof(Update) };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = nameof(Delete) };
}
..and then create our AuthorizationHandler:
public class OrderCreatorAuthorizationHandler :
AuthorizationHandler<OperationAuthorizationRequirement, Order>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
InspectionManagementUser resource)
{
if (context.User == null || resource == null)
{
return Task.CompletedTask;
}
var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (resource.CreatedById == currentUserId
&& requirement.Name == CrudOperations.Read.Name) {
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
This is registered as a service in Startup.cs, and is ready to go. In our view logic, we can use our new handler to get a filtered list of orders as such:
//_context is an injected instance of the application's DatabaseContext
//_authorizationService is an injected instance of IAuthorizationService
var allOrders = await _context.Orders.ToListAsync();
var filteredOrders = allOrders
.Where(o => _authorizationService.AuthorizeAsync(User, o, CrudOperations.Read).Result.Succeeded);
This will work just fine, but to me seems extremely computationally expensive as each record is checked separately in memory. This would increase even further as the logic for the authorization handler got more complex (for example, if it involved a database call).
It would presumably be far more efficient to have the database engine filter the list for us as follows:
var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var filteredOrders = await _context.Orders
.Where(o => o.CreatedById == currentUserId)
.ToListAsync();
This will execute faster, but we've now bypassed our authorization logic completely. If we later decide to change the restrictions in our AuthorizationHandler we must also remember to change it here and anywhere else we use this method. If you ask me that rather seems to defeat the purpose of separating this authorization code out in the first place.
Is there a neat solution to this problem that I'm missing? Any advice or guidance on best practice would be much appreciated.
Helloo everybody. Tell me please. Can I generate a Sample Request in Swagger depending on the model, so as not to write it manually. To see which fields the model has.
Is it even possible?
Because now I have to manually write a description of the request for each api.
I would like to automate this process.
I am using Swashbuckle and ASP.NET Core
There is a place for code examples, you should add /// <example>123</example> on the model instead of the remarks.
Is this close to what you need:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=Company#/Company/Company_Post
The code for that is here:
https://github.com/heldersepu/Swagger-Net-Test/blob/c9b554fde26367418c84fcd3682d308ae1b40d11/Swagger_Test/Models/Company.cs
You can also make dynamic changes with an IDocumentFilter like this:
private class AddExampleDocumentFilter : IDocumentFilter
{
private List<Guid> Guids
{
get
{
return new List<Guid>
{
Guid.Empty, Guid.Empty
};
}
}
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
{
if (swaggerDoc.paths.ContainsKey("/api/Dictionary"))
{
var del = swaggerDoc.paths["/api/Dictionary"].delete;
if (del != null)
{
del.parameters[0].schema.example = Guids;
}
}
}
}
Here is the result of that:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=Dictionary#/Dictionary/Dictionary_DeleteEcho
And once the user executes it with the sample data the UI shows the curl request:
I am new to web APIs and my design might be plain wrong, so feel free to correct my idea. We are building a series of web APIs, and we need to implement for most of them an excel and CSV export. What I imagined was that I could list all the routes in my application, and dynamically add new routes that handle the excel/CSV conversion.
For example, if I have a route api/customers, I want my service to create a new route on its own, which will be api/customers/excel and will return the same data as an excel file. This new route will take all the arguments of the initial route plus the names of the columns to export.
Adding the routes went kinda alright (even though I did not test it extensively and I might have added bugs with my copy ?) :
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
public override IReadOnlyList<RouteEntry> GetDirectRoutes(HttpControllerDescriptor controllerDescriptor, IReadOnlyList<HttpActionDescriptor> actionDescriptors,
IInlineConstraintResolver constraintResolver)
{
var routes = base.GetDirectRoutes(controllerDescriptor, actionDescriptors, constraintResolver);
var result = new List<RouteEntry>();
foreach (var routeEntry in routes)
{
result.Add(routeEntry);
var defaults = new HttpRouteValueDictionary(routeEntry.Route.Defaults.ToDictionary(kv => kv.Key, kv => kv.Value));
var constraints = new HttpRouteValueDictionary(routeEntry.Route.Constraints.ToDictionary(kv => kv.Key, kv => kv.Value));
var dataTokens = new HttpRouteValueDictionary(routeEntry.Route.DataTokens.ToDictionary(kv => kv.Key, kv => kv.Value));
var copy = new RouteEntry(routeEntry.Name,
new HttpRoute(
routeEntry.Route.RouteTemplate + "/csv",
defaults,
constraints,
dataTokens
));
result.Add(copy);
}
return new ReadOnlyCollection<RouteEntry>(result);
}
}
public class OwinConfiguration
{
public void Configure(IAppBuilder appBuilder)
{
/* lots of stuff */
var inlineConstraintResolver = new DefaultInlineConstraintResolver();
var directRouteProvider = new CustomDirectRouteProvider();
config.MapHttpAttributeRoutes(inlineConstraintResolver, directRouteProvider);
}
}
What I would like to do now is to use the underlying handler for the newly created routes, and then call my conversion method :
return response.AsCsvExport(columnHeaders);
on the HttpResponseMessage. For now, the route copy does the same thing as the reference route. If I try to add a handler in the HttpRoute constructor :
public class CsvExportDelegatingHandler: DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var route = request.GetRouteData().Route.RouteTemplate;
var response = await base.SendAsync(request, cancellationToken);
return response.AsCsvExport();
}
}
var handler = HttpClientFactory.CreatePipeline(new HttpControllerDispatcher(Configuration), new DelegatingHandler[] { new CsvExportDelegatingHandler() });
my app fails to initialize. Is there any way to do that?
Our service is built with WepApi 2, Owin and Autofac if that matters.