Implementing a net-core Web API That Returns Previous Response - api

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.

Related

Converting API from REST-like routing to named parameters

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

JSON.NET JsonIgnore DeserializeObject

I created a Web API using VS 2012. I have a method with a custom object parameter that I am passing JSON to via Fiddler for testing:
[HttpPost, HttpPut]
public HttpResponseMessage UpsertProject(Projects p)
{
...
}
My Projects object has about a dozen properties marked as JsonIgnore. My assumption was that when my object was serialized into Json those properties would be ignored...which is true. However, when I debug my method I'm noticing that all the object properties marked with JsonIgnore are set to null even if the Json that I pass in from Fiddler is setting them. I also try to get data as Json and deserialize it into a new instance of the object but that also does not set the properties that are marked JsonIngore. I knew JsonIgnore would work for serializing but didn't think it would prevent properties from being set when deserializing. What's frustrating is I know that ScriptIgnore doesn't behave this way, but I want to use JSON.net to handle my serializing/deserializing. I've also created a windows app and tested the same serializing/deserializing functionality and it works in it. So I'm wondering if this is a Web API limitation with the JsonIgnore attribute?
If it works the way you want in the Windows application but not in the Web API, that tells me that the JSON serializer settings are different between the two. What settings are you using in the Windows app that makes it work? You can take those settings and apply them to the Web API serializer in the Register method of the WebApiConfig class (in the App_Start folder of your Web API project). For example:
JsonSerializerSettings jsonSettings = config.Formatters.JsonFormatter.SerializerSettings;
jsonSettings.NullValueHandling = NullValueHandling.Ignore;
jsonSettings.DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate;
...
EDIT
OK, so if I understand you correctly, based on your most recent comments, you want everything to be deserialized if possible, but you only want two specific properties to be serialized and those apparently do not have null or default values. In that case, there are two approaches you can take:
Set the properties that you don't want serialized to null or zero or false (the default value) just before serializing. Because you have DefaultValueHandling set to Ignore, this will cause those properties not to be serialized.
Create several boolean ShouldSerializeXXX() methods in your class where XXX is the name of each property you don't want serialized. These methods should return false. See the first answer of this question for an example.
Don't use JsonIgnore because, as you have seen, this will cause the property to be completely ignored by Json.Net, both for serializing and deserializing.

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.

Inject behavior into WCF After or During identification of WebGet Method to call

I am trying to solve a problem where i have a WCF system that i have built a custom Host, Factory host, instance providers and service behaviors to do authentication and dependency injection.
However I have come up with a problem at the authorisation level as I would like to do authorisation at the level of the method being called.
For example
[OperationContract]
[WebGet(UriTemplate = "/{ConstituentNumber}/")]
public Constituent GetConstituent(string ConstituentNumber)
{
Authorisation.Factory.Instance.IsAuthorised(MethodBase.GetCurrentMethod().Name, WebOperationContext.Current.IncomingRequest.Headers["Authorization"]);
return constituentSoapService.GetConstituentDetails(ConstituentNumber);
}
Basically I now have to copy the Call to IsAuthorised across every web method I have. This has two problems.
It is not very testable. I Have extracted the dependecies as best that I can. But this setup means that I have to mock out calls to the database and calls to the
WebOperationContext.
I Have to Copy that Method over and over again.
What I would like to know is, is there a spot in the WCF pipeline that enables me to know which method is about to be called. Execute the authorisation request. and then execute the method based on the true false value of the authorisation response.
Even better if i can build an attribute that will say how to evaluate the method.
One possible way to do what you want might be by intercepting requests with a custom IDispatchMessageInspector (or similar WCF extension point).
The trick there, however, is that all you get is the raw message, but not where it will be processed (i.e. the method name). With a bit of work, however, it should be possible to build a map of URIs/actions and the matching method names (this is how you'd do it for SOAP, though haven't tried it for WebGet/WebInvoke yet).