In a given example code below the metadata page never gets the description specified in
[Description("GET account, all or by list of groups or by list of logins")]
Is there a special config that needs to be set in order for descriptions to show up in the metadata pages?
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using ServiceStack.ServiceHost;
using System.Runtime.Serialization;
using ServiceStack.WebHost.Endpoints;
namespace ConsoleApplication2
{
public class User
{
public User()
{
}
public int login;
public string group;
public string name;
}
[Description("GET account, all or by list of groups or by list of logins")]
[Route("/accounts")]
public class Accounts : IReturn<List<User>>
{
public string[] groups { set; get; }
public int[] logins { set; get; }
public Accounts() { }
public Accounts(params int[] logins)
{
this.logins = logins;
}
public Accounts(params string[] groups)
{
this.groups = groups;
}
}
public class Host : AppHostHttpListenerBase
{
public Host() : base("Test",
typeof(Accounts).Assembly)
{
}
public override void Configure(Funq.Container container)
{
SetConfig(new EndpointHostConfig {
EnableFeatures = Feature.All
});
}
}
public class Servce : IService
{
public object Get(Accounts request)
{
return new List<User>(){new User()};
}
}
class Program
{
static void Main(string[] args)
{
var host = new Host();
host.Init();
host.Start("http://+:12345/");
Console.ReadLine();
}
}
}
Navigating to http://localhost:12345/json/metadata?op=Accounts produces
<body>
<a id="logo" href="http://www.servicestack.net" title="servicestack"></a>
<h1>Test</h1>
<form>
<div>
<p><back to all web services</p>
<h2>Accounts</h2>
<div class="example">
<!-- REST Examples -->
...
In a recent release of ServiceStack, [Description] was deprecated in favour of [Api] and [ApiMember] which are also used in ServiceStack's Swagger support.
This is now an example of a fully annotated service:
[Api("Service Description")]
[Route("/swagger/{Name}", "GET", Summary = #"GET Summary", Notes = "GET Notes")]
[Route("/swagger/{Name}", "POST", Summary = #"POST Summary", Notes = "POST Notes")]
public class MyRequestDto
{
[ApiMember(Name="Name", Description = "Name Description",
ParameterType = "path", DataType = "string", IsRequired = true)]
public string Name { get; set; }
}
Related
Can I use AutoMapper 8.0.1 with Blazor server app, please?
I have try it but my code always run into an error:
Missing type map configuration or unsupported mapping. Mapping types:
Object -> Object System.Object -> System.Object
I have added the mapper to the Startup file:
services.AddAutoMapper(typeof(Startup));
I have created the profile:
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<District, DistrictModel>();
}
}
And I try to use it:
[Inject]
protected IMapper Mapper { get; set; }
District district = DistrictService.FindDistrictById(districtId);
DistrictModel model = Mapper.Map<DistrictModel>(district);
The AssertConfigurationIsValid method gives:
Cannot find any profiles with the name 'MyProfile'. (Parameter 'profileName')
Add this in your services in startup :
it's reusable and cleaner
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
}
add these to interface and class in your project
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
using AutoMapper;
using System;
using System.Linq;
using System.Reflection;
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.GetInterfaces()
.Any(i =>i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetMethod("Mapping")
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
methodInfo?.Invoke(instance, new object[] { this });
}
}
}
your model or viewmodel :
public class District : IMapFrom<District>
{
public string PhoneNumber { get; set; }
public string Password { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<District, DistrictModel>();
}
}
Startup.cs
var mapperConfiguration = new MapperConfiguration(configuration =>
{
configuration.AddProfile(new MyProfile());
});
var mapper = mapperConfiguration.CreateMapper();
services.AddSingleton(mapper);
I am trying to resolve list of object using autofac Container, and expecting an empty list in response. However, I am not able to get empty list in return instead getting count as 1.
I also try with without list registration in aotufac conatiner but getting same response.
<pre><code>
class autofacFactory : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>));
builder.RegisterType<Response>().As<IResponse>();
builder.RegisterType<CustomDependencyResolver>().As<ICustomDependencyResolver>();
}
}
public class Response : IResponse
{
public string TransactionNo { get; set; }
public string SchemeCode { get; set; }
}
public interface IResponse
{
string TransactionNo { get; set; }
string SchemeCode { get; set; }
}
public interface ICustomDependencyResolver
{
TResolved Resolve<TResolved>();
}
internal class CustomDependencyResolver : ICustomDependencyResolver
{
private readonly ILifetimeScope _lifetimeScope;
public CustomDependencyResolver(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
public TResolved Resolve<TResolved>()
=> _lifetimeScope.Resolve<TResolved>();
}
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule(new autofacFactory());
using (var container = builder.Build())
{
ICustomDependencyResolver customDependencyResolver = container.Resolve<ICustomDependencyResolver>();
var collection = customDependencyResolver.Resolve<ICollection<IResponse>>();
var list = customDependencyResolver.Resolve<IList<IResponse>>();
}
}
Actual response:
[Image1][1]
[Image2][2]
[Expected Response][3]
[1]: https://i.stack.imgur.com/NVXeW.jpg
[2]: https://i.stack.imgur.com/k58QX.jpg
[3]: https://i.stack.imgur.com/EcFyc.jpg
Try not registering IList<> or List<> - Autofac has built-in support for that.
I am trying to setup a modular ServiceStack implementation but I can't seem to figure out how to address my plug-in.
Here is my ASP.Net MVC 4 Global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
{
[Route("/heartbeat")]
public class HeartBeat
{
}
public class HeartBeatResponse
{
public bool IsAlive { get; set; }
}
public class ApiService : Service
{
public object Any(HeartBeat request)
{
var settings = new AppSettings();
return new HeartBeatResponse { IsAlive = true };
}
}
public class AppHost : AppHostBase
{
public AppHost() : base("Api Services", typeof(ApiService).Assembly) { }
public override void Configure(Funq.Container container)
{
Plugins.Add(new ValidationFeature());
Plugins.Add(new StoreServices());
}
}
protected void Application_Start()
{
new AppHost().Init();
}
This loads fine and I'm able to see the available "HeartBeat" Service. The service loaded by the plug-in is not found though.
Here is the plug-in code:
public class StoreServices: IPlugin
{
private IAppHost _appHost;
public void Register(IAppHost appHost)
{
if(null==appHost)
throw new ArgumentNullException("appHost");
_appHost = appHost;
_appHost.RegisterService<StoreService>("/stores");
}
}
and the corresponding service that it loads:
public class StoreService:Service
{
public Messages.StoreResponse Get(Messages.Store request)
{
var store = new Messages.Store {Name = "My Store", City = "Somewhere In", State = "NY"};
return new Messages.StoreResponse {Store = store};
}
}
[Route("/{State}/{City}/{Name*}")]
[Route("/{id}")]
public class Store : IReturn<StoreResponse>
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
}
public class StoreResponse
{
public Store Store { get; set; }
}
The url to run heartbeat is from localhost}/heartbeat and the meta can be found at from localhost}/metadata.
When I try to call {from localhost}/stores/1234 though I get a unresolved route?, but if you see the route attribute on the service call it should resolve?
The following is the response I get for the stores request:
Handler for Request not found:
Request.ApplicationPath: /
Request.CurrentExecutionFilePath: /stores/123
Request.FilePath: /stores/123
Request.HttpMethod: GET
Request.MapPath('~'): C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api\
Request.Path: /stores/123
Request.PathInfo:
Request.ResolvedPathInfo: /stores/123
Request.PhysicalPath: C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api\stores\123
Request.PhysicalApplicationPath: C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api\
Request.QueryString:
Request.RawUrl: /stores/123
Request.Url.AbsoluteUri: http://localhost:55810/stores/123
Request.Url.AbsolutePath: /stores/123
Request.Url.Fragment:
Request.Url.Host: localhost
Request.Url.LocalPath: /stores/123
Request.Url.Port: 55810
Request.Url.Query:
Request.Url.Scheme: http
Request.Url.Segments: System.String[]
App.IsIntegratedPipeline: True
App.WebHostPhysicalPath: C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api
App.WebHostRootFileNames: [global.asax,global.asax.cs,packages.config,spiritshop.api.csproj,spiritshop.api.csproj.user,spiritshop.api.csproj.vspscc,web.config,web.debug.config,web.release.config,api,app_data,bin,obj,properties]
App.DefaultHandler: metadata
App.DebugLastHandlerArgs: GET|/stores/123|C:\Source Code\White Rabbit\SpiritShop\SpiritShop.Api\stores\123
This code doesn't does not give your service a url prefix like you're assuming:
_appHost.RegisterService<StoreService>("/stores");
Instead the optional params string[] atRestPaths only specifies routes for the DefaultRequest route of that Service. You can specify which operation is the default using the [DeafultRequest] attribute, e.g:
[DefaultRequest(typeof(Store))]
public class StoreService : Service { ... }
Which allows you to specify the routes in-line instead of on the request DTO, i.e:
_appHost.RegisterService<StoreService>(
"/stores/{State}/{City}/{Name*}",
"/stores/{Id}");
But as you've already got the routes on the Request DTO you can ignore them here, i.e:
_appHost.RegisterService<StoreService>();
But you'll need to include the missing /stores url prefix, e.g:
[Route("/stores/{State}/{City}/{Name*}")]
[Route("/stores/{Id}")]
public class Store : IReturn<StoreResponse> { .. }
I was playin' around with ServiceStack and was wondering if it supported this scenario. I'm using generics in my request types so that many DTOs that inherit from a common interface will support the same basic methods [ like... GetById(int Id) ].
Using a request type specific to a single kind of DTO works, but breaks the generics nice-ness...
var fetchedPerson = client.Get<PersonDto>(new PersonDtoGetById() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson.Id)); //PASS
Mapping a route to the generic also works:
Routes.Add<DtoGetById<PersonDto>>("/persons/{Id}", ApplyTo.Get);
...
var fetchedPerson2 = client.Get<PersonDto>(string.Format("/persons/{0}", person.Id));
Assert.That(person.Id, Is.EqualTo(fetchedPerson2.Id)); //PASS
But using the end-to-end generic request type fails:
var fetchedPerson3 = client.Get<PersonDto>(new DtoGetById<PersonDto>() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson3.Id)); //FAIL
I wonder if I'm just missing something, or if i'm trying to abstract just ooone layer too far... :)
Below is a complete, failing program using NUnit, default ServiceStack stuff:
namespace ssgenerics
{
using NUnit.Framework;
using ServiceStack.ServiceClient.Web;
using ServiceStack.ServiceHost;
using ServiceStack.ServiceInterface;
using ServiceStack.WebHost.Endpoints;
[TestFixture]
class Program
{
public static PersonDto GetNewTestPersonDto()
{
return new PersonDto()
{
Id = 123,
Name = "Joe Blow",
Occupation = "Software Developer"
};
}
static void Main(string[] args)
{}
[Test]
public void TestPutGet()
{
var listeningOn = "http://*:1337/";
var appHost = new AppHost();
appHost.Init();
appHost.Start(listeningOn);
try
{
var BaseUri = "http://localhost:1337/";
var client = new JsvServiceClient(BaseUri);
var person = GetNewTestPersonDto();
client.Put(person);
var fetchedPerson = client.Get<PersonDto>(new PersonDtoGetById() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson.Id));
var fetchedPerson2 = client.Get<PersonDto>(string.Format("/persons/{0}", person.Id));
Assert.That(person.Id, Is.EqualTo(fetchedPerson2.Id));
Assert.That(person.Name, Is.EqualTo(fetchedPerson2.Name));
Assert.That(person.Occupation, Is.EqualTo(fetchedPerson2.Occupation));
var fetchedPerson3 = client.Get<PersonDto>(new DtoGetById<PersonDto>() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson3.Id));
Assert.That(person.Name, Is.EqualTo(fetchedPerson3.Name));
Assert.That(person.Occupation, Is.EqualTo(fetchedPerson3.Occupation));
}
finally
{
appHost.Stop();
}
}
}
public interface IDto : IReturnVoid
{
int Id { get; set; }
}
public class PersonDto : IDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Occupation { get; set; }
}
public class DtoGetById<T> : IReturn<T> where T : IDto { public int Id { get; set; } }
public class PersonDtoGetById : IReturn<PersonDto> { public int Id { get; set; } }
public abstract class DtoService<T> : Service where T : IDto
{
public abstract T Get(DtoGetById<T> Id);
public abstract void Put(T putter);
}
public class PersonService : DtoService<PersonDto>
{
public override PersonDto Get(DtoGetById<PersonDto> Id)
{
//--would retrieve from data persistence layer
return Program.GetNewTestPersonDto();
}
public PersonDto Get(PersonDtoGetById Id)
{
return Program.GetNewTestPersonDto();
}
public override void Put(PersonDto putter)
{
//--would persist to data persistence layer
}
}
public class AppHost : AppHostHttpListenerBase
{
public AppHost()
: base("Test HttpListener",
typeof(PersonService).Assembly
) { }
public override void Configure(Funq.Container container)
{
Routes.Add<DtoGetById<PersonDto>>("/persons/{Id}", ApplyTo.Get);
}
}
}
No, It's a fundamental concept in ServiceStack that each Service requires its own unique Request DTO, see this answer for more examples on this.
You could do:
[Route("/persons/{Id}", "GET")]
public class Persons : DtoGetById<Person> { ... }
But I strongly advise against using inheritance in DTOs. Property declaration is like a DSL for a service contract and its not something that should be hidden.
For more details see this answer on the purpose of DTO's in Services.
Seeing a strange problem, getting empty metata pages for xml,json and jvs.
Using the following command line app. How does one debug these issues?
namespace ConsoleApplication2
{
public struct NativeUser
{
public int login;
public string group;
public string name;
}
[DataContract]
public class User
{
private NativeUser _native;
public User() { }
public User(NativeUser native)
{
_native = native;
}
public static implicit operator NativeUser(User user)
{
return user._native;
}
public static implicit operator User(NativeUser native)
{
return new User(native);
}
// ReSharper disable InconsistentNaming
[DataMember]
public int login
{
get { return _native.login; }
set { _native.login = value; }
}
[DataMember]
public string group
{
get { return _native.group; }
set { _native.group = value; }
}
[DataMember]
public string name
{
get { return _native.name; }
set { _native.name = value; }
}
}
[Description("GET account, all or by list of groups or by list of logins")]
[Route("/accounts/{groups}", "GET")]
[Route("/accounts/{logins}", "GET")]
[Route("/accounts/", "GET")]
public class Accounts : IReturn<User[]>
{
public string[] groups { set; get; }
public int[] logins { set; get; }
public Accounts() { }
public Accounts(params int[] logins)
{
this.logins = logins;
}
public Accounts(params string[] groups)
{
this.groups = groups;
}
}
public class Host : AppHostHttpListenerBase
{
public Host() : base("Test",
typeof(Accounts).Assembly)
{
}
public override void Configure(Funq.Container container)
{
}
}
public class Servce : IService
{
public object Get(Accounts request)
{
return new List<User>(){new User(new NativeUser())};
}
}
class Program
{
static void Main(string[] args)
{
var host = new Host();
host.Init();
host.Start("http://+:12345/");
global::System.Console.ReadLine();
}
}
}
Nm, found the bug :
public class Accounts : IReturn<User[]>
needs to be
public class Accounts : IReturn<List<User>>
Another very note worthy thing: All DTO's and objects being passed back and fourth in the DTO's require an empty constructor in order for the metata data to be properly generated.
Not sure if this is by design or a bug