Refit API Client - Mix Body and Queryparameters in single object - asp.net

When using the Refit REST Library to build a client targeting an asp.net based api is it possible to mix Query and Body items in a single Custom Object?
Example Asp.Net Route
[Route("documents/{id}")]
public void PostDocument(int id, [FromBody] document)
Example Refit Client and Request Object showing desired mixing of Body and Query definition in single object
[Post("/documents/{document.id}}")]
Task PostDocument(PostDocumentRequest request);
public class PostDocumentRequest
{
public int Id {get;set;}
public [Query] DateTime? LocalDateTime {get;set;}
public [Body] Document Document {get;set;}
}

Related

When to use FromUri and when not in an ASP.NET Web API

I have a working code with some routes like:
[Route("Companies/{id}", Name = "CompanyDetails")]
[HttpGet]
public HttpResponseMessage getCompanyDetails(string id)
{...}
[Route("Drivers/{driverIds}/Routes", Name = "Drivers")]
[HttpGet]
public HttpResponseMessage getDrivers([FromUri] List<int> driversId)
{...}
[Route("Vehicles/Signs", Name = "VehicleSigns")]
[HttpGet]
public HttpResponseMessage getVehicleSigns([FromUri]string companyId , [FromUri]List<string> vehicleIds)
{...}
From a similar question i learnt that it has to do with the type of the parameter , still i haven't configured out in depth when i have to put [FromUri] and when not .Does it have to do with the parameter type , the number of parameters or a combination of them?
It is to do with how Web API binds parameters for an action method.
If you look at the official docs:
If the parameter is a "simple" type, Web API tries to get the value
from the URI. Simple types include the .NET primitive types (int,
bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal,
and string, plus any type with a type converter that can convert from
a string. (More about type converters later.)
For complex types, Web
API tries to read the value from the message body, using a media-type
formatter.
So, to bind a complex type from Uri parameters:
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public ValuesController : ApiController
{
public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}
And then pass the Latitude and Longitude in URI query string:
http://localhost/api/values/?Latitude=47.678558&Longitude=-122.130989
If you don't specify [FromUri] attribute web api will look for Longitude and Latitude in the request body by default

How does breeze detemine API action method based on entity name?

I have a simple entity: Contact
DBContext line:
public DbSet<Contact> Contacts { get; set; }
API Controller:
[HttpGet]
public IQueryable<Contact> Contacts()
{
return _contextProvider.Context.Contacts;
}
In my data retrieval in breeze client I do this:
var query = EntityQuery.from("Contacts")
.orderBy(orderBy.obContact)
That works great, and I can understand that the from parameter "Contacts" must match the API action method.
In my getByID I do this:
return manager.fetchEntityByKey("Contact", contactId, true)
That's working great, and it also makes a call to the "Contacts" API method. But I would like to know how breeze took the parameter "Contact" and knew to call the "Contacts" method.
Is this in the metadata?
Did it come from my DBSet line in my dbcontext? (I'm thinking it did, but would like confirmation). If that's the case then these two names must be equal right?
[HttpGet]
public IQueryable<Contact>Contacts() // The HTTP action method name
public DbSet<Contact> Contacts { get; set; } // The DbSet variable
I tried these changes:
public DbSet<Contact> DBSetContacts { get; set; }
and
[HttpGet]
public IQueryable<Contact> Contacts()
{
return _contextProvider.Context.DBSetContacts;
}
My first query above that returns an array ran fine.
My fetch by ID failed, it was trying to find URI resource "DBSetContacts".
My conclusion is that the DbSet variable has to have the same name as the URI method for the fetchByID to work. Is that correct?
Breeze internally keeps an EntityType/ResourceName map. ResourceNames are the names of the server side methods.
The EntityType/ResourceName map is one of the items in the Breeze MetadataStore. The map of a new MetadataStore starts empty. Breeze populates it from server metadata if those metadata contain EntityType/Resource mappings.
As you guessed, the Breeze EFContextProvider generates metadata with mappings derived from Entity Framework DbSet names. When you define a Person class and exposed it from a DbContext as a DbSet named "Persons", the EFContextProvider metadata generator adds a mapping from the "Persons" resource name to the Person entity type.
For more information see the 'EntityType/ResourceName" mapping subtopic on this page.
http://www.getbreezenow.com/documentation/querying-locally
You can also update the EntityType/ResourceMap explicitly via the method below:
http://www.getbreezenow.com/sites/all/apidocs/classes/MetadataStore.html#method_setEntityTypeForResourceName

ServiceStack DTO Model Binding for Route Parameters AND Body

I have a Request DTO set up for performing a PUT against a service that results in an update.
I require both route parameters AND a json payload to be sent as the PUT (this payload is the ApprovalRoleData object below, and represents the new state of the object I want to have reflected on the server):
[Route("/qms/{QAID}/reviewers/{RoleType}", "PUT")]
public class UpdateReviewer
{
public string QAID { get; set; }
public string RoleType { get; set; }
public ApprovalRoleData UpdatedRoleData { get; set; }
}
Within my service, I have a Put() call that accepts this DTO: The issue is that the ApprovalRoleData object is not being deserialized (but the QAID and RoleType are):
public object Put(UpdateReviewer request)
{
string QAID = request.QAID; //can see value
string RT = request.RoleType; //can see value
ApprovalRoleData ard = request.UpdatedRoleData; //null
}
Is there a way like in WebAPI to specify that I want model binding to work with both route parameters AND a body?
Side Note:
Also, getting the underlying stream so I can just parse myself with base.RequestContext.Get<IHttpRequest>().InputStream didn't work since there was no remaining stream to read (i'm assuming the part of ServiceStack that does the model binding probably consumed the stream by the time I got to it?)

Json object using WebAPI controller

I am using webapi controller.I have to pass an JSON object from Java applet to web api controller. How to write a web api controller method to accept the JSON object
public test GetPo(int id)
{
}
ASP.NET Web API uses JSON format as default format:
JSON formatting is provided by the JsonMediaTypeFormatter class. By default, JsonMediaTypeFormatter uses the Json.NET library to perform serialization. Json.NET is a third-party open source project.
what you just do is to defined your model object which map with json:
public class Model
{
public int Property1 { get; set; }
public string Property2 { get; set; }
// More properties
}
And your POST method:
public void Post(Model model)
{}

Deserializing JSON objects as List<type> not working with asmx service

I am having trouble deserializing my JSON string. I have a class of type person with public properties for sequence number of type int, first name, and last name. I want to pass an array of these objects in JSON format and have them deserialized as a list so I can loop through them on the server, but ASP.NET says something about not being supported to be deserialized as an array. I have validated the JSON I am producing, and it is valid. Is there something special about the JSON that ASP.NET needs to have before it can deserialize? The funny thing is if I serialize a list<person> object to JSON it looks exactly like the JSON I am producing. I must be missing something... To clarify, I'm using the ASP.NET Ajax library to deserialize. This is what I get back from the web service:
{"Message":"Type \u0027System.Collections.Generic.IDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]\u0027 is not supported for deserialization of an array."
Actually unfortunately this doesn't seem to have anything to do with deserializing, it appears that you can't pass an array of JSON objects to an asmx web service. Am I correct? If you can't do that, is it possible to pass a collection of JSON objects to a web service and have them processed on the server with ASP.NET and C#?
Update:
OK, here is my code. Here is the person class:
public class person
{
public person()
{
//
// TODO: Add constructor logic here
//
}
public int seq
{
get;
set;
}
public string firstName
{
get;
set;
}
public string lastName
{
get;
set;
}
}
And here is my JSON string:
[{"seq":1,"firstName":"Chris","lastName":"Westbrook"},
{"seq":2,"firstName":"sayyl","lastName":"westbrook"}]
And here is the code I'm using
[WebMethod]
public void updatePeople(string json)
{
IList<person> people =
new JavaScriptSerializer().Deserialize<IList<person>>(json);
//do stuff...
}
I figured it out. I wasn't wrapping my JSON in an object like ASP.NET Ajax requires. For future viewers of this question, all JSON objects must be wrapped with a main object before being sent to the web service. The easiest way to do this is to create the object in JavaScript and use something like json2.js to stringify it. Also, if using an asmx web service, the objects must have a __type attribute to be serialized properly. An example of this might be:
var person=new object;
person.firstName="chris";
person.lastName="Westbrook";
person.seq=-1;
var data=new object;
data.p=person;
JSON.stringify(data);
This will create an object called p that will wrap a person object. This can then be linked to a parameter p in the web service. Lists of type person are made similarly, accept using an array of persons instead of just a single object. I hope this helps someone.
Could you show the JSON string you are trying to deserialize and the way you are using the Deserialize method? The following works fine:
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
namespace Test
{
class Program
{
class Person
{
public int SequenceNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static void Main()
{
string json = "[{\"SequenceNumber\":1,\"FirstName\":\"FN1\",\"LastName\":\"LN1\"},{\"SequenceNumber\":2,\"FirstName\":\"FN2\",\"LastName\":\"LN2\"}]";
IList<Person> persons = new JavaScriptSerializer()
.Deserialize<IList<Person>>(json);
Console.WriteLine(persons.Count);
}
}
}
Or even simpler, when you are doing the $.ajax(...) use
data:"{\"key\":"+JSON.stringify(json_array)+"}",
and then on the other side of the code, make your function use the parameter "object key"
[WebMethod]
public static return_type myfunction(object key){...}
SERVER SIDE
[WebMethod]
public void updatePeople(object json)
CLIENT SIDE
var person = "[{"seq":1,"firstName":"Chris","lastName":"Westbrook"}
,{"seq":2,"firstName":"sayyl","lastName":"westbrook"}]";
var str = "{'json':'" + JSON.stringify(person) + "'}";
I think the problem is what type you have to deserialize. You are trying to deserialize type
IList
but you should try to deserialize just
List
Since interface can not be instantiated this might is the root problem.

Resources