Converting API from REST-like routing to named parameters - asp.net-web-api2

I have created an API that looks for parameters as such ...
http://server/api/contollerName/param1/param2/param3
for this pattern I used the following method attribute:
[Route("api/myController/{param1}/{param2}/{param3}")]
[HttpGet]
public IHttpActionResult myMethod(int param1, int param2, int param3)
{
// Method implementation goes here
}
However the person who will be utilizing the API (the UI developer) would prefer to have the API setup as this ...
http://server/api/controllerName?param1=value&param2=value&param3=value
Am I correct in thinking that all I need to do is remove the attribute to my method and the Web Api routing engine will choose the correct method based on the parameter names? Is there a way I could modify the route to explicitly define the method and parameters like I have originally done? I like having the route specified so there is no ambiguity as to which method will be executed.

You are correct in thinking that all you need to do is to remove the attribute to your method. You would not be able to define a route template of the format
http://server/api/controllerName?param1=value&param2=value&param3=value
because ASP.Net Web API2 routing template does not allow to contain a "?" character in the route template. The closest you can get to is
http://server/api/controllerName/MethodName/param1={param1},param2={param2}
Also notice that if you have & in the routing template, ASP.NET will throw an error that dangerous Request.Path value was detected, when you make the request to hit that API. This can be resolved though by referring to this SO Question

Related

How can I get all Razor Page routing endpoints and filter for one with a specific attribute value

I have a custom attribute I want to apply to a Razor Page like this
#attribute [PageKey("PageKeyToSearchFor")]
Then from another Razor Page I want to search all my Razor Pages for the one that has the key I'm looking for
You can get an EndpointDataSource from dependency injection which will give you access to all registered endpoints in the routing system.
For each endpoint you can then check for your attribute by using the endpoint.Metadata.GetMetadata<TYourAttribute>() method then compare against the value if GetMetadata was not null.
NOTE: If you are looking at Razor Page endpoints or anything else that might be dynamically compiled then the endpoint from EndpointDataSource might not have all the metadata you would expect. To get the full metadata you need to use the PageLoader class (available from DI as well) to get the full endpoint with all it's metadata. For example:
var pageActionDescriptor = endpoint.Metadata.GetMetadata<PageActionDescriptor>();
var endpointWithFullMetadata = await pageLoader.LoadAsync(pageActionDescriptor)

In AspNetCore, what is this currently called and can I encapsulate it?

I'm currently making a library that interacts with a particular API that requires an oAuth OIDC connection/token and I'd like to make something that makes that particular part easier for users of this library so they don't need all this custom code.
What is this currently called and is there an example of code?
I ask because from trying to get this work, the documentation is ALL OVER THE PLACE. It looks like this particular process has undergone significant changes multiple times as things went on from before netcore to netcore2 and now netcore31.
Both AddAuthentication and AddOpenIdConnect are extension methods.
An extension method allows you to "add" methods to a type without having to modify the type directly - the methods aren't actually added to the type, but we can call them as if they had been. This is useful in situations where you'd like to extend the behaviour of a class created by a third party but don't have access to the source code.
As for what the particular pattern in question is, whilst there is no canonical name, it's merely a way of encapsulating the configuration of services and dependencies for your application.
AddAuthentication is an extension method of IServiceCollection, which is services type. AddAuthentication has a return type of AuthenticationBuilder, and AddOpenIdConnect is an extension method for AuthenticationBuilder. This is the implementation of AddOpenIdConnect, but as you're looking to encapsulate the configuration, you could probably do something like this:
public static class OpenIdConnectExtensions
{
public static AuthenticationBuilder ConfigureOpendIdConnect(
this AuthenticationBuilder builder)
{
return builder.AddOpenIdConnect(options =>
{
options.SignInScheme = IdentityConstants.ExternalScheme;
// Do whatever else you need.
});
}
}
And call it as follows:
services
.AddAuthentication()
.ConfigureOpendIdConnect()
Middleware, on the other hand, is code that executes as part of a pipeline in order to process requests and responses. The middleware sits in the middle of receiving requests and sending responses, hence the name. This allows you do things such as always adding certain headers to responses without that logic being split across your application. As you correctly mentioned, you see these being applied via calls such as app.UseXyz().

Implementing a net-core Web API That Returns Previous Response

I'm currently building a Web API in net-core that has the following requirements:
All web transactions must have a unique Guid identifier for each endpoint
If an endpoint is hit with a previously used Guid, then the response that was given for this Guid is returned again
I was attempting to implement this by JsonSerializing the IActionResult inside the WebApi controller, but I ran into an issue where I can't deserialize all IActionResult responses since some don't have a constructor.
For example:
JsonSerializationException: Unable to find a constructor to use for type Microsoft.AspNetCore.Mvc.CreatedResult. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.
Does anybody know if it's possible to work around this?
Personally, I'd use the response caching middleware. You can make it vary on your GUID, so as long as that is included in the request, it will only render the action if the GUID is different.
Short of that, if you want to handle this manually, cache the JSON you're intending to return, not the full response object. Then, you do not need to re-query the database, etc., and you simply return the response, which is not all that much overhead.

Searching with WebAPI

I have made a web API class for my Customer model. I have the standard methods (GET, POST, PUT, DELETE). The problem is, I want to implement another GET method which is a search. Something like this:
[HttpGet]
public IEnumerable<Customer> Search(string id)
{
var customers = customerRepository.Search(id);
return customers;
}
The search method performs a search based on the account number of my customers, using the .Contains() method.
The problem is, when I navigate to: mySite.com/api/Customers/Search/123 I get a 404. What am I doing wrong here?
While Darin's answers are always of top quality this question would actually benefit from an answer that explains how searching, paging and filtering should actually be done in any API and how it should be done using the most current version of Web API (v2).
This is a post which I consider a good resource on the matter (technology indenpendent):
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
The answer should also reflect what's new in ASP.NET Web API v2 because Darin's answer is quite old.
Since this question comes up at the top when doing Google search for "asp.net web api searching" I will try to explain few things here.
To get as close as possible to REST principles with the latest version of ASP.NET Web API (v2) one should take a serious look at attribute routing that was introduced in the latest version. It is very hard to achieve RESTful routing with the old, classic, convention based routing (in global.asax.cs or RouteConfig.cs).
You should read more about that here
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
Now, to go into details how to implement the specifics you ask about.
The most common practice is to expose these types of functionality through query string parameters.
Per REST principles, you should have one endpoint for your Customers resource, for instance
/api/customers
To achieve this you would decorate your GetCustomers() action in your Web API controller like this
[HttpGet]
[Route("/api/customers")]
public HttpResponseMessage GetCustomers(string q="", string sortBy="", string sortDirection="", bool active=true, ...)
{
// q = being optional search query
// sortBy = optional sort by column/property
// sortDirection = optional sort direction
// active = filter on 'active' column/property
// ... other filters may be applicable
}
You would implement this action closely to what you did in classic MVC if you wanted to provide filtered Views.
I would only introduce new controllers and custom actions if really needed, for some custom edge cases.
with regards to a comment about SearchFilter strongly typed object, let's explain that this won't work out of the box because the default model binder will not bind to this class when using GET requests.
So I'd either take those properties out of SearchFilter class and put them on the action itself so they'd bind via query string binder or use the [FromBody] binder if you wanted to bind from the request body. As per http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
HTH
As per the default route setup only the standard controller action names are allowed (the RESTful ones and the dispatching is done based on the HTTP verb). If you want to violate the RESTful conventions and use some custom action names then you will have to modify your route setup in order to include the action name in the url: api/{controller}/{action}/{id}. Now you can send a request to /api/Customers/Search/123 which will invoke the Search action on the Customers API controller.

ModelState.AddModelError

Am I missing something? I am trying to return model validation errors from my web api update method like this
public void Update public void UpdateModel(Models.Model entity) {
ModelState.AddModelError("name","error");
return;
However the JSON returned has no validation errors
For example in this article enter link description here
When I try to use a validation filter as in the previous article on this link I get the error Action xxxxx has one or more filters applied that do not derive from AuthorizationFilterAttribute. Only authorization filters are supported on DataController Insert/Update/Delete actions.
ModelState isnt accessible from your JSON, unless you specifically serialize it as such. It's more used from the HtmlHelper extensions behind the scenes. If you're attempting to update the UI as if the model state was marked as such, you're going about it all wrong. You can either:
Do a full post to the server and let it render the appropriate response
Take the json you get back and then style the fields as appropriate for that response.