I'm having issues with displaying the correct notes in swagger using ServiceStack.
Given this structure:
[Route("/Widget/{WidgetId}", Summary = "Updates a widget by id", Verbs = "POST",
Notes = "Updates a widget by id.")]
public class UpdateReqWidget : IReturnVoid
{
[ApiMember(Name = "WidgetId", Description = "The id of widget to delete.")]
public int WidgetId { get; set; }
}
public class WidgetService
{
public void Put(UpdateReqWidget req)
{
//do code here
}
}
It's producing:
I would expect the parameters list only to have WidgetId, but it's displaying WidgetId and UpdageReqWidget, the class name for the request. any ideas what I'm doing wrong?
EDIT: I'm using versions 3.9.55 for both ServiceStack and ServiceStack.API.Swagger. I've changed the templates to better suit our needs.
There is a recent addition to ServiceStack.Api.Swagger to automatically generate a request body parameter in the Swagger UI. I think there's an edge case in which the request DTO has no properties that aren't designated as path or query parameters, as in your case here, the code will still generate that UpdateReqWidget request body parameter, but it will be an empty object in Swagger.
Related
With ServiceStack's Razor Story we have a variety of ways of selecting which Razor View we want to use to render a page. Even better, and critical in my case, is we can pass in a Content-Type header (or query string parameter, or even page "suffix") as well to return the raw model in a variety of formats.
Is there any way to use ServiceStack Templates (now known as SharpScript) to do the same thing? I follow the example here but I just get back the standard HTML format response. It doesn't use my template, no matter how named.
Following the example in the v5.5 Release Notes:
[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
public class HelloService : Service
{
public object Any(Hello request) => new HelloResponse { Result = $"Hello, {request.Name}!" };
}
Going to /hello/World?format=html provides me the standard HTML report, not my template. I followed another example to force it to use the template ....
public object Any(Hello request) =>
new PageResult(Request.GetPage("examples/hello")) {
Model = request.Name
};
... and it ALWAYS returns my template, even if I specify /hello/World?format=json.
Is there any way to have Razor-like view selection for ServiceStack + ScriptSharp pages, but also support different response formats?
It's hard to answer a vague question like this without details of a specific scenario you want to achieve that's not working.
You can return Sharp Pages in a number of ways:
When it's requested directly as a content page, e.g /dir/page -> /dir/page.html
Using Page Based Routing, e.g /dir/1 -> /dir/_id.html
As a View Page in response to a Service when it's named after the Request DTO or Response DTO, e.g /contacts/1 -> /Views/GetContact.html or /Views/GetContactResponse.html
Select which view to render inside your Service by returning your Response DTO inside a custom HttpResult:
public object Any(MyRequest request)
{
...
return new HttpResult(response)
{
View = "CustomPage", // -> /Views/CustomPage.html
//Template = "_custom-layout",
};
}
Add the [ClientCanSwapTemplates] Request Filter attribute to let the View and Template by modified on the QueryString, e.g: ?View=CustomPage&Template=_custom-layout
[ClientCanSwapTemplates]
public object Any(MyRequest request) => ...
Choosing which page you want to render inside your Model View Controller Service by returning a custom PageResult:
public class CustomerServices : Service
{
public object Any(ViewCustomer request) =>
new PageResult(Request.GetPage("examples/customer")) {
Model = TemplateQueryData.GetCustomer(request.Id)
};
}
Note: That the SharpPagesFeature resolves pages using your cascading AppHost.VirtualFileSources. In .NET Core it's configured to use its WebRoot, e.g /wwwroot.
For Sharp Pages to return its Response in Multiple Content Types:
as well to return the raw model in a variety of formats.
You need to use a Sharp APIs which return a value, e.g. /hello/_name/index.html:
{{ { result: `Hello, ${name}!` } | return }}
To succinctly answer my own question, the first option from #mythz is what I needed. After calling Plugins.Add(new SharpPagesFeature()) in my AppHost, I needed to return HttpResult from my service method:
public object Any(MyRequest request)
{
...
return new HttpResult(response)
{
View = "CustomPage", // -> /Views/CustomPage.html
//Template = "_custom-layout",
};
}
I am trying to call a method in a webapiController(.net core) from my test
If my request object has an Id as string it does not work ,as an int it does
what Am I doing wrong in my noddy sample?
[Fact]
public async Task WhyDoesNotWorkWithIdAsString()
{
string thisQueryDoesNotWork = "http://localhost:1111/api/v1/shop/customers?id=1";
string thisQueryWorksProvidedTheIdIsAnInt = "http://localhost:1111/api/v1/shop/customers/1";
var response = await client.GetAsync(thisQueryDoesNotWork);
var response2 = await client.GetAsync(thisQueryWorksProvidedTheIdIsAnInt);
//omitted asserts
}
[Route("api/[controller]")]
public class ShopController: Controller
{
[HttpGet]
[Route("{id}",Name ="GetCustomerAsync")]
[ProducesResponseType(typeof(GetCustomerResponse), (int)HttpStatusCode.OK)]
//more ProducesResponseType omitted
public async Task<IActionResult> GetCustomerAsync([FromQuery]GetCustomerRequest request)
{
//code omitted
}
}
public class GetCustomerRequest
{
Required]
public string Id { get; set; }
// public int Id { get; set; } //works with int but not with a string
}
}
Also is below correct
[FromQuery]=use Get only
[FromBody]=use Put-Post
is there a link with explanation when to use this parameter binding?
many thanks
In
[Route("{id}",Name ="GetCustomerAsync")]
{id} template parameter is part of the route but in the action parameter is it being requested via [FromQuery], which is why it is not matching.
It is expecting
http://localhost:1111/api/v1/shop/customers/1
But your are sending
http://localhost:1111/api/v1/shop/customers?id=1
which is why the second link works and the first does not.
Reference Routing to Controller Actions
As for the concern about [From*] attributes
[FromHeader], [FromQuery], [FromRoute], [FromForm]: Use these to specify the exact binding source you want to apply.
...
[FromBody]: Use the configured formatters to bind data from the request body. The formatter is selected based on content type of the request.
Reference Model Binding in ASP.NET Core
I have found out what the problem is, Name of the httpget or route must match the one you setup in the link
I have a custom content type built through the UI (e.g. not via a module) that has a couple of fields on it, one of which is a ContentItemPicker. I managed to get everything with the front-end working for this with the exception of finding the friendly URL off of the ContentItem from the Model's collection of items. I'm seeing some examples where I'm supposed to use Url.ImageDisplayUrl([ContentItem]), but that gives me this error: 'System.Web.Mvc.UrlHelper' has no applicable method named 'ItemDisplayUrl' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
My using statements at the top are as follows:
#using Orchard.ContentPicker.Fields
#using Orchard.Utility.Extensions;
#using System.Linq
#using Orchard.ContentManagement;
#using Orchard.Mvc.Html;
#using Orchard.Utility.Extensions;
I'd assume I'm missing something with those, but can't seem to figure out what. The way I'm building out my view is below, and the URL I am trying to get is off of tab.ContentItem.HomepageTab.NavigationItem:
/** #Model.Items is a collection of my custom content types that were created through the UI **/
foreach (var tab in #Model.Items)
{
var t = new SliderTab
{
DisplayOrder = tab.ContentItem.HomepageTab.DisplayOrder.Value,
ButtonText = tab.ContentItem.HomepageTab.ButtonText.Value,
Description = tab.ContentItem.HomepageTab.Description.Value,
ImageUrl = tab.ContentItem.HomepageTab.Image.Url,
Title = tab.ContentItem.TitlePart.Title,
ContentItem = tab.ContentItem,
TabText = tab.ContentItem.HomepageTab.TabText.Value
};
/** HomepageTab is the custom content type created in the Orchard UI which has a ContentPickerField associated with it. The name on that is NavigationItem, so I just need the friendly URL off of a ContentPickerField's associated ContentItem **/
if (tab.ContentItem.HomepageTab.NavigationItem != null && tab.ContentItem.HomepageTab.NavigationItem.Ids != null)
{
//this is way, super hacky - getting the actual friendly URL would be better
t.NavigateUrl = "/Contents/Item/Display/" + tab.ContentItem.HomepageTab.NavigationItem.Ids[0];
}
tabs.Add(t);
}
** Edit **
I have a class declaration for HomepageTab at the top which does not correlate to the tab.ContentItem.HomepageTag as that is dynamic off the ContentItem property. It is structured like this:
public class HomepageTab
{
public dynamic DisplayOrder { get; set; }
public string ImageUrl { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string ButtonText { get; set; }
public dynamic ContentItem { get; set; }
public string TabText { get; set; }
public string NavigateUrl { get; set; }
public string TabId
{
get { return "t" + this.DisplayOrder.ToString(); }
}
}
Thoughts?
tab.ContentItem.HomepageTab.NavigationItem is your content item picker field, but expressed this way, it's a dynamic object, so the compiler can get all sorts of confused if you try to use it without casting it. So first I'd recommend casting:
var pickerField = tab.ContentItem.HomepageTab.NavigationItem as ContentPickerField;
if (pickerField != null) {
Then you can get the first and only item in the field (caution, we're likely causing a select N+1 issue here, see below):
var firstItem = pickerField.ContentItems.FirstOrDefault();
Finally, we can ask for the display URL for that item:
if (firstItem != null) {
var url = Url.ItemDisplayUrl(firstItem);
This should work just fine. Be careful however: as I said above, getting the collection of items for each tab may trigger one new database query per tab, degrading performance. To avoid that problem, you could pre-fetch the related content items with a technique similar to what I describe in this post: https://weblogs.asp.net/bleroy/speeding-up-pages-with-lots-of-media-content-items
Instead of pre-fetching images, what you'd do here is first get a list of all the related ids (that's free, those ids come stored in the fields, which are stored with the content items you already have). Then you'd build a local cache of ids to items using a GetMany. And finally you'd use that cache instead of the ContentItems collection to look-up items from the ids.
I hope this makes sense.
I just downloaded ServiceStack with NuGet. Version 3.9.56.
I am trying simple webservice but when i open metadata json page it gives NullReferenceException error.
My service is here:
[Route("/users")]
[Alias("Users")]
public class User
{
[Alias("UserID")]
public int id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
public class UsersService:Service
{
public object Get(User request)
{
var result = new List<User>();
result = Db.Select<User>();
return result;
}
}
There is a known issue that might explain your null reference exception. However, you do not want IReturnVoid, unlike in your other post, so the answer here is not to wait for ServiceStack to be fixed but to improve your DTO's declaration:
Your UsersService implementation is returning a List<User> object for your User request. You can document this in ServiceStack like so:
public class User : IReturn<List<User>>
{
...
}
This may fix the issue you are seeing on the metadata page as ServiceStack now knows the type of response to expect for the User message. There are other benefits to decorating your request DTOs with IReturn:
The typed C# client will be easier to use, as the client can know the type of your response message
The Swagger UI, if you use it, will know about and automatically document the response type
I have the following Domain Model:
public class DaybookEnquiry : Entity
{
public DateTime EnquiryDate { get; set; }
[ForeignKey("EnquiryType")]
public int DaybookEnquiryTypeId { get; set; }
public string AccountNumber { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
#region Navigation Properties
public virtual User User { get; set; }
public virtual DaybookEnquiryType EnquiryType { get; set; }
public virtual ICollection<DaybookQuoteLine> QuoteLines { get; set; }
#endregion
}
This is inside of a project named DomainModel. Entity is just a base class which my domain models inherit from, it contains an Id field.
I then have other projects inside my solution called ServiceInterface and ServiceModel. ServiceInterface contains all my services for my application and ServiceModel contains my DTO's and routes etc.. I'm trying to follow the guidelines set out here: Physical Project Structure
My EnquiriesService contains a method to create a new enquiry in my database using a repository:
public void Post(CreateEnquiry request)
{
// Not sure what to do here..
// _repository.Insert(request);
}
My CreateEnquiry request looks like so:
[Api("POST a single Enquiry for Daybook.")]
[Route("/enquiries", "POST")]
public class CreateEnquiry : IReturnVoid { }
As you can see, the CreateEnquiry request object is empty. Do I need to add properties to it to match my Domain Model and then use AutoMapper or something similar to map the fields to my Domain Model and pass that into my repository?
The Insert method on my repository looks like so:
public virtual void Insert(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
DbContext.SaveChanges();
}
Yes. Your Service request, in this case CreateEnquiry needs to have all the properties you need in order to do whatever it is you want to do!
I've seen two different models for Create vs Update:
Use one request objects called, say, SetEnquiry that has a nullable id field. When null and using the POST HTTP verb, it internally creates a new object. And when not null and using the PATCH HTTP verb, it internally updates an object. You can use ServiceStack's implementation of AbstractValidator<T> to add logic such as if POST then id field needs to be null; and if PATCH then id field cannot be null. This will help ensure your data is always as it needs to be.
Create two request objects -- one for Create and one for Update. The Create doesn't even have an id field, and the Update has one and requires it. You can use the same validation technique used above, except applied to each class independently, so you don't need the conditional check of if this verb do this; if that verb do that.
How you map to your data model is up to you. You can use something like AutoMapper or you can use ServiceStack's built-in TranslateTo and PopulateWith methods. I personally take a middle ground: I created my own object extension methods called MapTo and MapFrom that interally call TranslateTo and PopulateWith respectively. Why did I do this? Because then I control those extensions inside my own namespaces and when I need to do special mappings (like a column name doesn't match up, or one object is more complex than the other, or I simply want to ignore a particular column from one of the objects) I simply overload the MapTo and MapFrom with explicit types, giving it higher specificity than the generic methods.
So back to your question specifically. Assuming you're using the built in TranslateTo your service method might look like this:
public void Post(CreateEnquiry request)
{
_repository.Insert(request.TranslateTo<Enquiry>());
}
One more thing: I generally return the object itself when doing a Create and Update. As fields can change (auto-calculated fields, for example) I like to return the object back to the caller. This is preference and has no real bearing on the answer I'm giving you. Just throwing it out there!