Routing with regular expressions in ServiceStack - c#

I'm trying to build a small .NET Core server with ServiceStack and would like to use regular expressions for routing. So far I've basically just created a Hello World project and connected it to database.
I have these classes in my ServiceModel project:
[Route("/hello/{Language}/{Name*}", Matches = #"PathInfo =~ \/hello\/[a-z]{2}\/[A-Za-z]+$")]
public class HelloTo : IReturn<HelloResponse> {
public string Language { get; set; }
public string Name { get; set; }
}
[Route("/hello/{Language*}", Matches = #"PathInfo =~ \/hello\/[a-z]{2}$")]
public class Hello : IReturn<HelloResponse> {
public string Language { get; set; }
}
public class HelloResponse {
public string Result { get; set; }
}
and this in my ServiceInterface project:
public HelloResponse Any(HelloTo request) {
var greeting = Db.SingleById<Greeting>(request.Language);
return new HelloResponse { Result = $"{greeting.Text}, {request.Name}!" };
}
public HelloResponse Any(Hello request) {
var greeting = Db.SingleById<Greeting>(request.Language);
return new HelloResponse { Result = $"{greeting.Text}, you!" };
}
The point is, when I send e.g. this request: http://localhost/hello/fr, it goes to the first route, even though it has no Name in it.
As far as I can tell, the second route is inaccesible with this configuration. After some experimentation, it seems to me as if the Matches parameter is completely ignored, which makes me think that maybe I need to enable something. However, I couldn't find much documentation on this, besides a short note here that routing using regexps should be possible somewhat like this.
Note that if I changed the second route from Route("/hello/{Language*}" to Route("/hello/{Language}", this sample will work as expected, presumably because routes without wildcards take precedence, but I need routes ending with wildcards like this for it to be of any practical use.

This issue was due to Matches rules not being validated for wildcard routes which is now resolved from this commit where your example will work as expected from ServiceStack v5.0.3 that's now available on MyGet.

Related

JSON Serialization of URI Type in WebAPI 2 ODATA 3 C# Project

I'm seeing C# URI types serialized to JSON in an ODATA 3 controller in my WebAPI 2 project as an array of segments that does not include the domain. I've tried everything I can think of to change this (including fiddling with the serialization settings and even trying out the contract serializer instead of JSON.Net). Nothing seems to change the behavior. Note, I am not using .Net Core. Here is a code sample, condensed into a single snippet.
namespace WebApplication1.Controllers
{
public class MyObject
{
public Uri Url { get; set; }
public string Name { get; set; }
public string ID { get; set; }
}
public class MyObjectsController : ODataController
{
private static ODataValidationSettings _validationSettings = new ODataValidationSettings();
public IHttpActionResult GetMyObjects(ODataQueryOptions<MyObject> queryOptions)
{
try
{
queryOptions.Validate(_validationSettings);
return Ok<IEnumerable<MyObject>>(new List<MyObject>() { new MyObject() { ID="asdf", Name="123rwe", Url = new Uri("http://www.webapp.com/sites/page.html") } });
}
catch (ODataException ex)
{
return BadRequest(ex.Message);
}
}
}
}
This generates the following JSON in the browser:
{
"odata.metadata":"http://localhost:51607/odata/$metadata#MyObjects","value":[
{
"Url":{
"Segments":[
"/","sites/","page.html"
]
},"Name":"123rwe","ID":"asdf"
}
]
}
This is what I'd like (without changing the Url property to a string):
{
"odata.metadata":"http://localhost:51607/odata/$metadata#MyObjects","value":[
{
"Url":"http://www.webapp.com/sites/page.html","Name":"123rwe","ID":"asdf"
}
]
}
Any thoughts?
UPDATE:
Further research is suggesting that serialization behavior for URI types in WebAPI ODATA is controlled by the odataentityreferencelink and odataentityreferencelinkserializer classes. Specifically, URI type appear to be converted to odataentityreferencelink types which are then serialized in the manner I posted above (as an array of segments not including the root domain). I still need to know how to change this behavior, however the documentation for these two classes is not proving helpful. Last, I've confirmed this problem is not specific to the JSON output format. The serialization behavior for both XML/Atom and JSON are identical: URIs are broken down into an array of segments.
MS Premier support provided a final answer to this which I'll share below.
There is no option to directly JSON serialize an URI type, normally it would be broken into array of segments as you are observing in your code
The domain name will be eliminated as a normal scenario
The option you can go for is to create a custom URI Converter deriving from JSONConverter which is a part of Newtonsoft.Json namespace

ASP.NET Swagger IEnumerable<T>

In Swagger UI I get a model like:
Inline Model [
Inline Model 1
]
Inline Model 1 {
Id (string, optional),
ConnectionString (string, optional),
ConnectionState (string, optional)
}
for a REST Get method like:
public IEnumerable<Device> Get()
{
return new List<Device>();
}
Why is it not displayed correctly?
Adding Swagger Config from comments
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration .EnableSwagger(c => { c.SingleApiVersion("v1", "api"); }) .EnableSwaggerUi(c => { });
}
}
public class Device
{
public string Id { get; set; }
public string ConnectionString { get; set; }
public string ConnectionState { get; set; }
}
In C# Asp.Net web api, I did this:
1- In SwaggerConfig.cs
.EnableSwagger(c =>
{//add this line
c.SchemaFilter<ApplyModelNameFilter>();
}
2- add a class that implements ISchemaFilter:
class ApplyModelNameFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
schema.title = type.Name;
}
}
I got the idea from here
It seems like Swagger and/or NSwag do not handle Generic List/IList/IEnumerable types very well as a base type, perhaps because some frameworks that may try to connect with Swagger don't understand them.
I have worked around this by wrapping my List in another object. So, in your case, you may need to do something like:
public ListResponseObject<T>()
{
public IEnumerable<T> ResponseList {get; set;}
}
And then return from your controller like this:
public ListResponseObject<Device> Get()
{
return new ListResponseObject<Device>{ResponseList = new List<Device>()};
}
Not as simple... but should get it through Swagger better.
We've leveraged this to our advantage. We've applied this technique to all controllers (or something similar) so we have a more standardized response. We can also do this:
public ListResponseObject<T>() : ResponseObject<T>
{
public IEnumerable<T> ResponseList {get; set;}
}
public ResponseObject<T>()
{
public string Message {get; set;}
public string Status {get; set;}
}
And now you have a container that will make downstream handling a little easier.
Not an exact answer, but a work-around that's worked for us. YMMV
UPDATE: Here's a response to a question I posted in the NSwag GitHub issues:
I think its correct as is. Currently swagger and json schema do not support generics (only for arrays) and thus all generic types are expanded to non-generic/specific types... altough the models should be correct but you may end up with lots of classes...
An enhancement for supporting generics is planned but this will be not compliant with swagger and only work with nswag... (No support in swagger ui)

serialized property to complex type in dto with linq and automapper

I'm having a hard time figuring something out that seems as a "easy" problem.
I'm working with Microsoft Azure mobile apps .Net backend, a MSSQL database, Entity Framework code-first and AutoMapper.
So i have the following objects:
public class Route
{
public string Id { get; set; }
[...] //some other properties
public string SerializedGoogleRoute { get; set; }
}
public class DtoRoute
{
public string Id { get; set; }
[...]
public DtoGoogleRoute GoogleRoute { get; set; }
}
public class DtoGoogleRoute
{
[...] //only strings, ints,...
}
So what I want to do is: In the database save the GoogleRoute as a serialized string because it consists of many properties and I don't need them in different columns - I just want it as a serialized string in one column on the route entity.
When the Route object is projected to the DtoRoute object I want the GoogleRoute to be serialized and vice versa.
Because I'm working with LINQ / queryables I am limited to a few AutoMapper mapping options (see AutoMapper wiki). And with none of these I can't get it to work.
The problems I'm facing/what I tried:
I can't serialize/deserialize the string to the DtoGoogleRoute on mapping (with MapFrom or ConstructProjectionUsing) because LINQ obviously cannot transform the JsonConvert.Serialize/Deserialize methods to SQL statements.
I tried having a DtoGoogleRoute property in the Route object and a string property in the DtoRoute object with getters/setters doing the (de)serialization. This works almost perfectly in a custom API controller but because of the OData query filter the azure mobile app .Net backend uses in the tablecontrollers again only the serialized string property gets returned to the client (because OData/LINQ does not know of the other property).
Another option was making a complex type out of DtoGoogleRoute with Entity Framework - this works fine but not with AutoMapper because AutoMapper can't handle complex types.
For now I'm working with a custom API controller and this works. But it would be better to use the tablecontrollers because they support offline sync.
I can't imagine such a simple thing (at least I thought it was a simple thing) can't be done or is so hard to do. But maybe the problem is all the components (tablecontroller, OData, LINQ, EF, AutoMapper) involved.
I would really be thankful if someone could help.
[EDIT]: I think the fact that it works with a normal api controller and not with a tablecontroller has something to do with OData. I tried putting the same code in a tablecontroller method and in an API controller method. when calling the API controller method I can see on the server that it just calls this function and returns all the right properties to the client (checked with fiddler). But when calling the tablecontroller method the tablecontroller method "rewrites" the URL to a OData URL --> I think this is because of some of the EnableQuery or other OData attributes. Because here (although not AutoMapper but it seems like a similar project from Microsoft) it says that the EnableQuery attribute is called twice - also when the request leaves the server. And I think it cuts of the GoogleRoute property because it does not know about this property in the OData metadata or something like that.
You can achieve it like this -
internal class RouteToDtoConverter : TypeConverter<Route, DtoRoute>
{
protected override DtoRoute ConvertCore(Route source)
{
return new DtoRoute
{
Id = source.Id,
GoogleRoute = JsonConvert.DeserializeObject<DtoGoogleRoute>(source.SerializedGoogleRoute)
};
}
}
internal class DtoToRouteConverter : TypeConverter<DtoRoute, Route>
{
protected override Route ConvertCore(DtoRoute source)
{
return new Route
{
Id = source.Id,
SerializedGoogleRoute = JsonConvert.SerializeObject(source.GoogleRoute)
};
}
}
public class Route
{
public string Id { get; set; }
public string SerializedGoogleRoute { get; set; }
}
public class DtoRoute
{
public string Id { get; set; }
public DtoGoogleRoute GoogleRoute { get; set; }
}
public class DtoGoogleRoute
{
public int MyProperty { get; set; }
public int MyProperty2 { get; set; }
}
AutoMapper.Mapper.CreateMap<Route, DtoRoute>()
.ConvertUsing(new RouteToDtoConverter());
AutoMapper.Mapper.CreateMap<DtoRoute, Route>()
.ConvertUsing(new DtoToRouteConverter());
var res = Mapper.Map<DtoRoute>(new Route
{
Id = "101",
SerializedGoogleRoute = "{'MyProperty':'90','MyProperty2':'09'}"
});
var org = Mapper.Map<Route>(res); //pass

How to avoid identical actions that do different things in RESTful Web API 2?

I'm in the process of designing a RESTful Web API and have bumped into the following problem: I need a controller to retrieve collections (called Sections) of a hierarchical structure as well as to retrieve a single part (a single Section). If I need a collection I have to refer to the ID of the root Section which gives me a subtree of the whole structure. So I went ahead and defined a SectionsController like this:
public class SectionsController : ApiController
{
// GET api/sections/5
// Gets a subtree.
public IEnumerable<Section> Get(int rootId)
{
...
}
// GET api/sections/5
// Gets a single section.
public Section Get(int sectionId)
{
...
}
Which obviously doesn't work as the signatures are identical. What is the recommended way to go about this?
If you want to follow standard REST patterns you should introduce a slightly different API:
public class SectionsController : ApiController
{
// GET api/section
public IEnumerable<Section> GetAll()
{
...
}
// GET api/section/5
public Section Get(int sectionId)
{
...
}
Normally you should use singular resources and provide identifier only for a specific one. You can't have same URLs, even with different controllers.
Reading this post on SO regarding image transfer and following the link provided I realized there is a very simple solution to this problem that respects REST and at the same time doesn't require additional controllers.
Just return a collection of the subtree IDs within the object requested for a particular ID, i.e.
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public int[] DescendantIds { get; set; }
}
So with a single call to
api/section/5
I get all the details for the section with ID 5 as well as the IDs of the sections below. Yes, there's some overhead involved, so you have to decide for yourself if this solution is for you.

Building json response

I'm working on a self hosting rest api used to monitor de status of several servers.
I was tasked that, when everything is working correctly, I should only return
{"response":"ok"}
But, when there's an error on queried server, or servers, I must return
{ "response" : [ {"agent":"<server>:<port>","port":"<port>" ,"Error":"<Description of the error>"} ] }
I was thinking on building a helper class to build object on this schema and returning them over the rest api
public class HelperErrorResponseClass
{
public string agent { get; set; }
public string port { get; set; }
public string Error { get; set; }
}
This is no problem, the issue is, how to deal when everything it ok. I have this Api response helper class
public class Response
{
public string response { get; set; }
}
But I'm seeing that I'll need to change the response property to List<HelperErrorResponseClass> in order to send the error response. Do you think that, if I stringify the List<HelperErrorResponseClass> object with Json.Net it will be returned in the desired format?
Edit: Forgot to add that, I-m using Web Api to build the rest service.
UDPATE:
After further research, I found a way to work this out.
Following this post, I was able to rewrite the helper classes like this
[DataContract]
[KnownType(typeof(List<HelperErrorResponseClass>))]
public class Response
{
[DataMember]
public object response { get; set; }
}
[DataContract]
public class HelperErrorResponseClass
{
[DataMember(EmitDefaultValue = false)]
public string agent { get; set; }
[DataMember(EmitDefaultValue = false)]
public string port { get; set; }
[DataMember(EmitDefaultValue = false)]
public string error { get; set; }
}
This work to fulfill my and my client needs... except for one little thing. When I get the result from a List, and given that I added the KnownTypes directive, my response is now this
{"response":[{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"},{"__type":"HelperErrorResponseClass:#AppCommonLib","Error":"ERROR","InstanceId":"<InstanceId> : <Port>","PortType":"<PortType>"}]}
Any idea how to get rid of that __type property of the response? make that it must be explicit to only return the declared properties of the helper class?
Simplest way to deal with this is to set the return type on the handling function to string, then you can check for errors and do something like;
//pseudo code to give an idea
if (errorsList.Count() > 0)
{
return JsonConvert.SerializeObject(errorsList);
}
else
{
return JsonConvert.SerializeObject(new Response("ok"));
}
Now this being said... Unless the people providing requirements aren't at all flexible you should just redo the design. How about just returning the errors array and the person calling the API can infer that if it's length is 0 then everything is working OK. Seems pretty straight forward, right? You could also just put all the properties on one object and those fields would just come back as null or empty strings. Or you could change you serializer settings to exclude them if they don't have a value.
Keep things simple and use an anonymous type.
if (condition)
{
return JsonConvert.SerializeObject(new { response = new { agent = "x", port = "y", error = "z" }});
}
else
{
return JsonConvert.SerializeObject(new { response = "ok"});
}
More info:
https://msdn.microsoft.com/en-us/library/bb397696.aspx
I personally don't think you need a Response class, especially that it is of object type. IMHO, you've overcomplicated the very simple issue that you have. It is not only the __type, but also other info like HelperErrorResponseClass:#AppCommonLib that isn't supposed to be there.
Another Issue you have is the incorrect name of the HelperErrorResponseClass class. This is not a helper class. It is a standard data-object class.
A helper class is a class filled with static methods. It is usually used to isolate a "useful" algorithm.
This is how I would do it:
I'd get rid of the Response class.
I'd use your original simple HelperErrorResponseClass class, but rename it to something more meaningful like ErrorDetails.
I'd return the response like this:
.
if (errorsList.Count() > 0) {
return JsonConvert.SerializeObject(new { response = errorsList});
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}
However, if you really want to stick to your updated solution, an easy way to get rid of the __type is simply removing it from the final serialized string:
if (errorsList.Count() > 0) {
string r = JsonConvert.SerializeObject(new { response = errorsList});
return r.Replace("__type", "");
}
else {
return JsonConvert.SerializeObject(new { response = "ok"});
}

Categories