Web API controller parameter is null only on GET request - rest

So I have a very simple controller that accepts a Search object
public class ProfileController : ApiController
{
[AcceptVerbs("GET", "POST")]
public async Task<ProfileDTO> GetProfile(Search profile)
{
//My code
}
}
The Search object only contains very simple, primitive data types
public class Search
{
public string Uuid { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public bool Censored { get; set; }
}
For testing purposes I am using Swagger to call my API with sample data. For some reason if I make the same call to api/profile with the same data the profile argument will only contain data during the POST call. On a GET it is always null.
I only added the AcceptVerbs("POST") to demonstrate this issue, but by RESTful design I want the endpoint to only accept GET verbs
As of right now I'm just sending some boilerplate sample data such as
{
"Uuid": "string",
"Email": "string",
"Name": "string",
"Censored": true
}
I know this is probably a very simple issue, but why is the parameter always null only on a GET request?

Related

POST parameter is null in when it's a hand made type

I have a asp.net 4.5.2 web api and one POST method receive a DTO parameter I've coded by hand like these:
using System;
namespace WebApi.Models.DTO
{
[Serializable]
public class MyModelDto
{
public MyModelDto()
{
}
public int Id { get; set; }
public string Code { get; set; }
public int Type { get; set; }
}
}
When I call this method from Postman the parameter is always null, but if I design the same DTO class in EntityFramewok (edmx model) and use it as parameter I could receive the data inside the post method as it was send.
I could not realize what I'm missing in my hand made class?
Why it works using EF and not with my class?
Setting the parameter as dynamic also works...
[HttpPost]
[JwtAuthentication]
[Route("api/bills/invoice")]
public JsonResult<WebApi.Models.DTO.MyModelDto> Post(WebApi.Models.DTO.MyModelDto param)
{
// Here param is always NULL
}
[HttpPost]
[JwtAuthentication]
[Route("api/bills/invoice")]
public JsonResult<Data.MyModelDto> Post(Data.MyModelDto param)
{
// Here param wors! based on the EF class
}
public JsonResult<Data.MyModelDto> Post(dynamic param)
{
// Here param wors! using dynamic data type
}
it depends how you send the data via Postman.
I don't have Postman here to test but make sure you select Json Type when sending data and then your sent data needs to match your DTO. Something like:
{
"Id":1,
"Type":"whatever",
"Code":"whatever"
}
This data goes in the body of the request since you are sending a Post request.
One extra observation, your DTO does not need an empty constructor, you can get rid of it. There is nothing wrong with:
public class MyModelDto
{
public int Id { get; set; }
public string Code { get; set; }
public int Type { get; set; }
}
For testing purposes get rid of your Jwt authentication, until you get the calls running correctly

How to pass query string parameter to asp.net web api 2

How can I pass more than 1 parameters as part of query string to my asp.net web api 2.
This is my asp.net web api 2 method, I am not able to figure out that how can I decorate this method so that it accepts the id and a complex type which is CustomerRequest, I want to use Url something like
http://localhost/api/Customer/?Mobile0012565987&Email=abcxyz.com&IsEmailVerified=true
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomer(long id, [FromUri]CustomerRequest request)
{
var customer = db.Customers.Find(request.CustomerId);
if (customer == null)
{
return NotFound();
}
return Ok(customer);
}
This is CustomerRequest class
public class CustomerRequest
{
public string Mobile { get; set; }
public string Email { get; set; }
public Nullable<bool> IsEmailVerified { get; set; }
}
Otherwise pleaase guide me if there is a better way to do it.
Thanks
Based on your code, you need to pass 'id' as well, like this:
http://localhost/api/Customer/?id=12345&Mobile=0012565987&Email=abcxyz.com&IsEmailVerified=true
if you want to make 'id' optional, you can make your method signature look like this:
public IHttpActionResult GetCustomer([FromUri]CustomerRequest request, long id = 0)
this will set id to 0 by default, if you dont pass it in the URL. So you will be able to access your URL like you originally did:
http://localhost/api/Customer/?Mobile=0012565987&Email=abcxyz.com&IsEmailVerified=true

Attaching to existing object in a many to many relation with Entitiy Framework Code First

I'm working on a Rest API using Web API2 and EF6 Code first starting from the guide on http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4
I'm basically doing the same thing having a many to many relationship and when i am creating an object, lets call it A, i include an array of B objects as the Bs variable in the post. They all get created as expected on the initial post, however when i add a second Object A which should link to one or more of the same B's as the first object A it instead of matching to the existing B's tries to create new B's but since there is a constraint on the name of the B this wont work. How do i ensure that it does not try to create a new object B every-time and instead link to an existing Object B if there is one?.
Here is the example in more detail.
I have two Models, lets call them A and B. They have a many to many relation
public class A
{
public int Id { get; set; }
[Required, StringLength(100), Index("IDX_Name", 2, IsUnique = true)]
public string Name { get; set; }
[StringLength(300)]
public string Description { get; set; }
public ICollection<B> Bs{ get; set; }
}
Model B
public class B
{
public int Id { get; set; }
[Required, StringLength(100), Index("IDX_Name", 2, IsUnique = true)]
public string Name { get; set; }
public ICollection<B> As{ get; set; }
}
I'm not including the auto generated context.
and in the auto generated Controller scaffolding for the Web API POST method for Model A it looks like this
[ResponseType(typeof(A))]
public async Task<IHttpActionResult> PostGame(A a)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.As.Add(a);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = a.Id }, a);
}
All tables are created just fine and if i do the first post creating my first object A with the following json:
{
"Name": "FirstA",
"Description": "FirstADesc",
"Bs" : [{"Name":"FirstB"}]
}
It works and both FirstA and FirstB is created.
If i then post a SecondA which is also linked to the FirstB obect
{
"Name": "SecondA",
"Description": "SecondADesc",
"Bs" : [{"Name":"SecondB"},{"Name":"FirstB"}]
}
It will instead of finding the FirstB try to create it again. Which it due to the constraint.
My first guess was that i should use the ID:s instead in the second post. like:
{
"Name": "SecondA",
"Description": "SecondADesc",
"Bs" : [{"Id":"1"},{"Name":"FirstB"}]
}
but this does not work either.
Is the only way of achieving this to replace the scaffolding code from the controller and check each object in Bs manually if it exist already?.
Basically its a "Tags to a post problem"...
I've looked into the Attached vs Detached Data subject and read articles on the matter without finding an answer that i can understand whether this can be done automatically with some proper annotations or if it has to be done "manually" in the controller.
Thanks in Advance!
Not much response on this one,
Ended up looping and manually checking for an existing entry like below.
[ResponseType(typeof(A))]
public async Task<IHttpActionResult> PostGame(A a)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
foreach (var asd in a.Bs.ToList())
{
var t = db.Bs.FirstOrDefault(a => a.Name == asd.Name);
if (t != null)
{
a.Bs.Remove(asd);
a.Bs.Add(t);
}
}
db.As.Add(a);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = a.Id }, a);
}
Cant help to feel that there has to be a better way then this though.

How can I translate an href into a RequestDto using ServiceStack?

I'm building a ReST API that supports linked resource expansion, and I can't work out how to use ServiceStack's native binding capabilities to translate a URL into a populated 'request DTO' object.
For example, say my API allowed you to retrieve information about a band using this request:
GET /bands/123
< 200 OK
< Content-Type: application/json
{
"href": "/bands/123",
"name": "Van Halen",
"genre": "Rock",
"albums" {
"href" : "/bands/1/albums",
}
}
If you wanted to expand the band's album list, you could do this:
GET /bands/1?expand=albums
< 200 OK
< Content-Type: application/json
{
"href": "/bands/123",
"name": "Van Halen",
"genre": "Rock",
"albums" {
"href" : "/bands/1/albums",
"items": [
{ "href" : "/bands/1/albums/17892" },
{ "href" : "/bands/1/albums/28971" }
]
}
}
I'm using ServiceStack, and I'd like to perform this inline expansion by re-using existing service methods.
My ServiceStack response DTOs look like this:
public class BandDto {
public string Href { get; set; }
public string Name { get; set; }
public AlbumListDto Albums { get; set; }
}
public class AlbumListDto {
public string Href { get; set; }
public IList<AlbumDto> Items { get; set;}
}
public class AlbumDto {
public string Href { get; set; }
public string Name { get; set; }
public int ReleaseYear { get; set; }
}
My ServiceStack request/route objects are like this:
[Route("/bands/{BandId}", "GET")]
public class Band : IReturn<BandDto> {
public string Expand { get; set; }
public int BandId { get; set; }
}
[Route("/bands/{BandId}/albums", "GET")]
public class BandAlbums : IReturn<AlbumListDto> {
public int BandId { get; set; }
}
and the actual services that handle the requests are like this:
public class BandAlbumService : Service {
public object Get(BandAlbums request) {
return(musicDb.GetAlbumsByBand(request.BandId));
}
}
public class BandService : Service {
private IMusicDatabase db;
private BandAlbumService bandAlbumService;
public BandService(IMusicDatabase musicDb, BandAlbumService bandAlbumService) {
this.db = musicDb;
this.bandAlbumService = bandAlbumService;
}
public object Get(Band request) {
var result = musicDb.GetBand(request.BandId);
if (request.Expand.Contains("albums")) {
// OK, I already have the string /bands/123/albums
// How do I translate this into a BandAlbums object
// so I can just invoke BandAlbumService.Get(albums)
var albumsRequest = Translate(result.Albums.Href);
result.Albums = bandAlbumService.Get(albumsRequest);
}
}
In the example above, say I have calculated the string /bands/123/albums as the HREF of Van Halen's album list.
How can I now use ServiceStack's built-in binding capabilities to translate the string /bands/123/albums into a BandAlbums 'request' object that I can pass directly into the BandAlbumService, get back a populated BandAlbumsDto object and include it in my response object?
(and yes, I'm aware this probably isn't an optimal approach in terms of minimising database hits. I'm going to worry about that later.)
RestPath should be able to help you:
I think this should work:
var restPath = EndpointHostConfig.Instance.Metadata.Routes.RestPaths.Single(x => x.RequestType == typeof(AlbumRequest));
var request = restPath.CreateRequest("/bands/123/albums")

WebApi OData: $filter 'any' or 'all' query not working

First, using the ASP.NET WebApi tutorials I've created a basic ApiController that exposes an Entity Framework model through OData. The service works to return json for OData $filter queries.
When I perform OData $filter queries that include "any" or "all" queryies on a multi-value property it throws an ODataException
Here's the OData query I'm trying to use
~/api/Blogs?$filter=any(Tags,Name+eq+'csharp')
My ApiController looks like this:
public class BlogController : ApiController
{
public BlogsController()
{
this.Entities = new BlogEntities();
}
public ContactEntities Entities { get; set; }
[Queryable(PageSize = 25, AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Blog> Get()
{
return this.Entities.Blogs;
}
}
The Blog entity has this contract
public Blog {
public Guid ID { get; set; }
public string Title { get; set; }
public Tag Tags { get; set; }
}
public Tag {
public Guid ID { get; set; }
public string Name { get; set; }
}
The exception thrown
ODataException: Type 'Blog' does not have a property 'Name'
As you can see, I have nothing out of the ordinary in my code and everything should work fine. Is it possible that "any" and "all" queryies aren't supported yet in Microsoft ASP.NET Web API OData?
Your any needs to be changed a bit. Try something like this:
~/api/Blogs?$filter=Tags/any(tag: tag/Name eq 'csharp')
This is assuming Tags actually returns a collection of Tags, not just a single Tag like you have above.
$inlinecount is only supported out of the box for the OData format. I wrote extensively about it here:
Web API OData Inlinecount not working
The short answer is that you can get it working for other formats with code that looks like this:
public PageResult<Customer> Get(ODataQueryOptions<Customer> queryOptions)
{
IQueryable results = queryOptions.ApplyTo(_customers.AsQueryable());
return new PageResult<Customer>(results as IEnumerable<Customer>, Request.GetNextPageLink(), Request.GetInlineCount());
}