ASP.NET WebAPI Supported Media Types per Method - asp.net-web-api

Given a method in a controller:
public class CustomerController : ApiController
{
[HttpGet]
public CustomerDto GetById([FromUri] int id)
{
.
.
return customerDto
}
}
Is there a way to specify supported Media Types with an attribute? For instance, CustomerDto is a complex class and will only serialize with JSON (application/json) not XML (application/xml) but could also accept PDF (application/pdf). Is there something like this:
[HttpGet(Accepts.JSON, Accepts.PDF)]
or
[HttpGet][AcceptJSON][AcceptXML]
or
[HttpGet][Accept("application/json")][Accept("application/pdf")]
If the incoming request wasn't supported a Unsupported Exception / Status could be returned.
Note - I don't want to remove say XML serialization all together as could be done globally. Instead, I would like to define what is accepted per route.
Using - ASP.NET WebAPI RC 1 (need to upgrade) + Self Hosting

Sounds like a custom ActionFilterAttribute might do the trick.
Create a new class that inherits from System.Web.Http.Filters.ActionFilterAttribute, override the OnActionExecuting method. Inside this method, you could check the request's headers, look for what you don't want to support and return an appropriate response.
The constructor for your custom ActionFilterAttribute could take the details of which "accept" types you want to process and which ones you want to reject.
For an example of a custom ActionFilterAttribute, check out this post.

Related

Accessing multiple controllers with same request mapping

Please find my HomeController and DemoController
class HomeController{
#RequestMapping(value="index")
public void home(){
}
}
class DemoController{
#RequestMapping(value="index")
public void demo(){
}
}
when I try to send a request to index, which one will get executed?
I wanted to know how can we have same request mapping value for multiple controllers
https://stackoverflow.com/a/34590355/2682499 is only partially correct at this point.
You can have multiple controller methods use the same URI so long as you provide Spring enough additional information on which one it should use. Whether or not you should do this is a different question. I would certainly not recommend using the same URI in two separate controller classes to avoid confusion, though.
You can do something like this:
class HomeController{
#RequestMapping(value="/index", params = {"!name", "!foo"})
public List<Something> listItems(){
// retrieve Something list
}
#RequestMapping(value="/index", params = "name")
public List<Something> listItems(String name) {
// retrieve Something list WHERE name LIKE %name%
}
#RequestMapping(value="/index", params = {"!name", "foo"})
public List<Something> listItems(String foo) {
// Do something completely different
}
}
For the full documentation on what is possible when overloading URIs you should reference the #ReqeustMapping documentation: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html. And, specifically https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#params-- for the section request parameters.
In Spring Web MVC this is not possible. Each mapping must be unique in your context. If not, you will receive a RuntimeException during context initialization.
You cannot even use parameters to differentiate your endpoints because they are not evaluated while searching for a suitable handler (applicable for Servlet environments). From #RequestMapping javadoc:
In a Servlet environment, parameter mappings are considered as restrictions that are enforced at the type level. The primary path mapping (i.e. the specified URI value) still has to uniquely identify the target handler, with parameter mappings simply expressing preconditions for invoking the handler.
Note that you can do the opposite, so multiple URLs can point to the same handler. Have a look at Spring MVC: Mapping Multiple URLs to Same Controller
Unfortunately, this is not possible. The request mapping has to be unique otherwise the application can't determine which method the incoming request should be mapped to.
What you can do instead is to extend the request mapping:
class HomeController{
#RequestMapping(value="home/index")
public void home(){
}
}
class DemoController{
#RequestMapping(value="demo/index")
public void demo(){
}
}

ObjectId Model Binding with Web Api 2

I'm using MongoDB with an ASP.NET Web Api (2) application, and want to accept ObjectId arguments in the Web Api methods.
I've written a custom model binder for the ObjectId type, and it when adding it to the Get method of a controller, everything works.
[Route("{id}")]
public HttpResponseMessage Get(String type, [ModelBinder(typeof(ObjectIdModelBinder))]ObjectId id) {
But I need to do this in several methods and controllers, so I would rather put it somewhere central. I've read that I can register the binder centrally like this:
public static void Register(HttpConfiguration config) {
var provider = new SimpleModelBinderProvider(typeof(ObjectId), new ObjectIdModelBinder());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
}
But that's not working!
Any ideas? Not really sure what the config.Services collection should contain, but I'm having a hard time locating the binder I insert.
With WebAPI even if you register a model binder, you still need to attach the [ModelBinder] to the input parameter, you just don't have to specify the type anymore so your method now looks like this:
[Route("{id}")]
public HttpResponseMessage Get(String type, [ModelBinder]ObjectId id) {
If you want to skip adding the attribute every time you declare a parameter of this type, then you have to look at writing a custom IActionValueBinder (which can be made very simple if you just extend the DefaultActionValueBinder) which is the default implementation. You might want to look at this post for pointers:
http://www.strathweb.com/2013/04/asp-net-web-api-parameter-binding-part-1-understanding-binding-from-uri/

read Asp.Net Web api GET values from url

I am trying to map /{Controller}/{Variable1}/{Variable2}/{Variable3} to a GET method in controller
public TestController{
public ActionResult Get([FromUrl] Entity instance){}
}
So I need to map variables to the entity.
To put it into an example
/Product/{category}/{filter1}/{filter2}/
Entity
public class ProductSearchRequest
{
public string Category{get;set;}
public string filter1 {get;set;}
public string filter2 {get;set;}
}
Controller
public ProductController: Controller {
public ActionResult GET([FromUri] ProductSearchRequest productSearchRequest){
}
}
[EDITED]
Had to do following changes to get this working
Instead of RouteCollection.MapHttpRoute use HttpConfiguration.Routes.MapHttpRoute as this is API routing not MVC routing.
Inherit controller from ApiController rather than Controller which I was before.
Basically you are not going to be able to do that. Complex types are not compatible with the routing mechanism.
Take a read of this article. But this paragraph explains why the routing mechanism cannot do what you are asking.
A complex type can only bind to the URI through a custom binding. But
in that case, the framework cannot know in advance whether the
parameter would bind to a particular URI. To find out, it would need
to invoke the binding. The goal of the selection algorithm is to
select an action from the static description, before invoking any
bindings. Therefore, complex types are excluded from the matching
algorithm.
Therefore the basic rule is:
For every parameter of the action, if the parameter is taken from the
URI, then the parameter name must be found either in the route
dictionary or in the URI query string. (Optional parameters and
parameters with complex types are excluded.)
Which means you need to define your action like so:
public ActionResult GET(string Category, string filter1, string filter2){
}
And your route template:
/{controller}/{category}/{filter1}/{filter2}/

Polymorphism in Web API: Single endpoint possible?

I realize that the Web API is REST focused, but I would still like to configure a single controller method that can handle a Command/Response scenario. So far I haven't been successful... is there a way to have the following class structure recognized by a single API endpoint?
[Serializable]
public abstract class Command{
public int CommandId{get; set;}
}
[Serializable]
public class RegisterNewPersonCommand:Command{
public string Name{get; set;}
}
//etc... various Command subclasses. Similar thing for Responses.
//now have a single endpoint to Handle Commands
public class CommandsController : ApiController{
public Response HandleCommand(Command command){
//handle Command based on which subclass it is
//return appropriate Response subclass
}
}
Thus far it doesn't seem the serialization system can handle this scenario, but I hope someone out there has found a way to do it :)
In order for polymorphism to work in Web API, you will need to enable type name handling and the data has to contain the type information.
You'll need to turn on TypeNameHandling in WebApiConfig.cs if you're using JSON in your scenario:
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling =
Newtonsoft.Json.TypeNameHandling.All;
Then, the content body that you are sending to your HandleCommand(...) action must contain the type information:
{"$type":"MvcApplication.Models.RegisterNewPersonCommand, MvcApplication", ... }
For XML, you'll need to use DataContract's KnownType...
By the way, is there any specific reason why you are using [Serializable] (since POCO types and [DataContract] types are also supported...)?

WebApi action parameters validation by ValidationAttribute

Does WebAPI can handle ValidationAttribute on action parameter?
For instance:
public class SampleController : ApiController
{
public string Get([RegularExpression("sampleExpression")]string id)
{
return "result";
}
}
In this sample WebAPI doesn't invoke any methods of RegularExpressionAttribute (or any other data annotation attribute) to validate input parameter. But in case if we passing an object as parameter, for instance a class then WebAPI can validate properties.
Is it a bug? Or I'm doing something wrong?
UPD: It's an open issue:
http://aspnetwebstack.codeplex.com/workitem/24?PendingVoteId=24
Does anyone can suggest a workaround?
This is a genuine question, I'm curious why not just do something like :
public class SampleController : ApiController
{
public string Get(string id)
{
RegularExpressionHelper.ValidateWith("sampleExpression",id);
return "result";
}
}
To me this seems to be equivalently concise. It is obvious when the validation occurs. I can safely assume that if the validation fails then an exception will be thrown. I can easily add additional validation and be confident of the order in which they occur.
Regarding the attribute method, I don't know if the validation is used as part of the route matching, I have no idea what happens if it fails. I don't know what filters run before or after the validation. I'm sure if I knew MVC better I would know the answer to these questions, but I don't see the advantage of using an attribute that makes my code's behaviour dependent on some framework controlled infrastructure.
Am I missing some significant benefit?
I had the same doubt. My workaround consists in creating a class just for encapsulating the parameter, so I can decorate it with the validation attribute I want. I could use the workaround proposed by Darrel in his answer, but I have a filter that checks if ModelState.IsValid before entering the action, so I need to validate before the action gets executed.
[ModelBinder]
public class Item
{
[RegularExpression("sampleExpression")]
public string Id { get; set; }
}
The class must be annotated with [ModelBinder], otherwise the parameter binding mechanism will try to extract the id field from the body of the request. Read this article for more info.
Also, note that Id is now in PascalCase instead of camelCase. Read this article to understand how the conversion is made.
The action signature is:
public string Get(Item item)

Resources