Resource Description on Web API Help page is showing "None." - asp.net-web-api

I'm having trouble figuring out how to get the Help page in my Web Api to show anything for Resource Description other than None. It has IHttpActionResult linked and then "None." after that. I got my samples working by adding config.SetActualResponseType(typeof(ComplexType), "Controller", "Action"); to HelpPageConfig.cs. My controller looks like this:
/// <summary>
/// My description
/// </summary>
[Route("MyRoute")]
public IHttpActionResult Get()
{
try
{
//throw new Exception("TEST");
return Ok(returnValue);
}
catch (Exception ex)
{
*Company Log Method*
return NotFound();
}
}
I don't know what I'm missing from HelpPageConfig.cs or any place else. Maybe a fresh set of eyes can catch something. Thanks in advance for the help!

I just needed to add [ResponseType(typeof(MyModel))] above my action after including using System.Web.Http.Description; at the top of my controller. I'm getting the description of my model now instead of IHttpActionResult and "None."

(This probably ought to be a comment but I don't have enough rep yet)
I'd start with break pointing the various points in Areas\HelpPage - e.g. XmlDocumentationProvider - to see if that gives you any clues. Nothing leaps out at me.
Could it be that you've changed where the XML help file is output and not updated the document path passed to XmlDocumentationProvider? The path is set through the Properties page - Output XML Documentation File.

Related

Primefaces timeline onchanged executes but does not call method on bean

I am attempting to add primefaces to an existing Spring/JSF (myfaces) project. I have the actual timeline component working (or at least loading with the correct data), but the event listeners don't seem to work correctly. I have tried a number of configurations but can't seem to resolve this issue.
<p:timeline id="timeline1"
value="#{timelineView.getModel(searchFacade.dataModel)}"
editable="true" eventMargin="10" eventMarginAxis="0"
start="#{timelineView.start}"
end="#{timelineView.end}"
showNavigation="true" showButtonNew="true"
axisOnTop="true" stackEvents="false"
oncomplete="styleEvents();">
<p:ajax event="changed" listener="#{timelineView.onEdit()}" oncomplete="restyleTimeline()" />
</p:timeline>
In the above code the changed event is triggered and there is an AJAX call made. In the network pane of the browser I can see that the AJAX call is always to the URL of the current page (so like /mypage.jsf?conversationCode=a - I'm also using Apache Orchestra obviously)
The actual form data being sent for the changed event looks like this:
javax.faces.partial.ajax:true
javax.faces.source:timeline1
javax.faces.partial.execute:timeline1
javax.faces.behavior.event:changed
javax.faces.partial.event:changed
timeline1_eventIdx:0
timeline1_startDate:1498881600000
timeline1_endDate:1510894800000
timeline1_group:<div class='timeline-group'><div class='timeline-row timeline-row-group'>Group One/div><div class='timeline-row timeline-row-sub'>Group One subtitle</div><div class='timeline-row timeline-row-details'>Group One details</div></div>
mainForm:j_id_mt_SUBMIT:1
javax.faces.ViewState: Big hash as usual
The bizarrely long group name is because I am formatting the output group names into a more detailed title for the row btw.
The bean in the back (imports and package omitted):
public class TimelineView<T> implements Serializable {
private static final long serialVersionUID = 1L;
TimelineView<T> timelineView;
public TimelineModel getModel() {
return timelineView.getModel();
}
public TimelineModel getModel(ListDataModel<? extends Row<T>> results) {
return timelineView.getModel(results);
}
public void setTimelineView(TimelineView<T> timelineView) {
this.timelineView = timelineView;
}
public Date getStart() {
return timelineView.getStart();
}
public Date getEnd() {
return timelineView.getEnd();
}
public void onEdit(TimelineModificationEvent e) {
timelineView.onEdit(e);
}
public void onSelect(TimelineSelectEvent e) {
timelineView.onSelect(e);
}
}
The functionality and code inside these methods doesn't matter because when I set a breakpoint I can see that the onEdit method is never called and neither was the onSelect method when I had a select event in the timeline.
Since I have Spring handling all beans, my configuration was just this, located in the ApplicationContext.xml file:
<bean id="timelineView" class="com.mycompany.projectone.view.timeline.TimelineView" />
I have tried adding a form around the timeline, which made no difference, and have also tried adding process=":form_id", which caused the error "Cannot find component for expression ":form_id"".
As an alternative that would also suit my needs, does anyone know how to send primefaces events to a javascript function to be handled? For example, if the user moves the start and end points of the item in the timeline I would like to update the displayed start and end dates.
I would also like to change or intercept the delete behavior and modify what occurs when the delete link is clicked.
Any help would be hugely appreciated. Thanks.
EDIT
The suggested potential duplicate does not address the symptoms nor the actual solution - see answer below.
Okay, so the answer turns out to be that the Primfaces timeline component must have a widgetVar set in order for the p:ajax and pe:javascript calls to work. Incredibly simple solution but so infuriatingly difficult to find.

UmbrellaException which contains ClassCastException when using GWTBootstrap3 Extras Summernote event handling (KeyUp Event)

I try to handle a Summernote Keyup event with this:
myEditor.addSummernoteKeyUpHandler(new SummernoteKeyUpHandler() {
#Override
public void onSummernoteKeyUp(final SummernoteKeyUpEvent event) {
// TODO Auto-generated method stub
log.fine("hello");
}
});
I get a UmbrellaException which is IMHO a class cast exception.
This is the call stack
I identified the following spot where te cast failes:
#HasNoSideEffects
static native boolean canCast(Object src, JavaScriptObject dstId) /*-{
if (#com.google.gwt.lang.Cast::instanceOfString(*)(src)) {
return !!#com.google.gwt.lang.Cast::stringCastMap[dstId];
} else if (src.#java.lang.Object::castableTypeMap) {
return !!src.#java.lang.Object::castableTypeMap[dstId]; //<-- this returns false!!!
} else if (#com.google.gwt.lang.Cast::instanceOfDouble(*)(src)) {
return !!#com.google.gwt.lang.Cast::doubleCastMap[dstId];
} else if (#com.google.gwt.lang.Cast::instanceOfBoolean(*)(src)) {
return !!#com.google.gwt.lang.Cast::booleanCastMap[dstId];
}
return false;
}-*/;
dstId contains:
Any help greatly appreciated!
I tested this with a small demo which actually works. But in my large application, I get this exception and I don't see why.
Do you have any idea whats wrong here?
Best regards
Hannes
As Andrei suggested I set the style to DETAILED. I use Eclipse as a development environment. I decided to clean build the system (which I had done before). Now the problem has simply vanished !! Furthermore, I use SDBG (see: https://sdbg.github.io/) to debug my GWT application. This works pretty well (even without -style DETAILED). Now the very very strange thing remains. I can set breakpoints for my application and they all work well, except setting a breakpoint within the event handling method. I use a logger to print some text to the console, so I see that the event handler for summernote is actually called but the debugger will not stop. I checked whether the breakpoint is listed in the tab "Breakpoints" and it is and it is checked. I don't get it. Perhaps I have to rebuild all again.
But to keep long things short:
The solution to the problem is probably to really issue a clean build and then hope for the best.

How to pursuade the ApiExplorer to create documentation for ExpandoObject?

I've created a very neat way of implementing a PATCH method for my Web.API project by making use of an ExpandoObject as a parameter. As illustrated below:
[HttpPatch, Route("api/employee/{id:int}")]
public IHttpActionResult Update(int id, [FromBody] ExpandoObject employee)
{
var source = Repository.FindEmployeeById(id);
Patch(employee, source);
Repository.SaveEmployee(source);
return Ok(source);
}
However, when generating documentation ApiExplorer is at a loss as to what to do with the ExpandoObject, which is totally understandable. Would anyone have any ideas on how to manipulate the ApiExplorer to provide some sensible documentation?
My idea was to maybe introduce an new attribute which points to the actual Type that is expected:
public IHttpActionResult Update(int id, [FromBody, Mimics(typeof(Employee))] ExpandoObject employee)
{
...
}
But I have no idea where to start, any ideas or suggestions are welcome.
So this has been the source of some late evenings in order to get the Api Explorer to play along with our developed Http Patch mechanism. Truth be told, I'd probably should do a bit of a proper write up to full explain the mechanics behind the whole idea. But for those of you who landed on this page because you want the Api explorer to use a different type in the documentation, this is where you need to look:
Open HelpPageConfigurationExtensions.cs and locate the following method:
//File: Areas/HelpPage/HelpPageConfigurationExtensions.cs
private static void GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator)
{
....
}
this is the location where the parameter information is available to you and also provides you with the ability to replace/substitute parameter information with something else. I ended up doing the following to handle my ExpandoObject parameter issue:
if (apiParameter.Source == ApiParameterSource.FromBody)
{
Type parameterType = apiParameter.ParameterDescriptor.ParameterType;
// do something different when dealing with parameters
// of type ExpandObject.
if (parameterType == typeof(ExpandoObject))
{
// if a request-type-attribute is defined, assume the parameter
// is the supposed to mimic the type defined.
var requestTypeAttribute = apiParameter.ParameterDescriptor.GetCustomAttributes<RequestTypeAttribute>().FirstOrDefault();
if (requestTypeAttribute != null)
{
parameterType = requestTypeAttribute.RequestType;
}
}
}
Just, note that the RequestTypeAttribute is something I devised. My WebApi endpoint looks like this now:
public IHttpActionResult Update(int id,
[FromBody, RequestType(typeof(Employee))] ExpandoObject employee)
Thank you to everyone who took time to look into the problem.

WebApi Help Page: don't escape HTML in XML documentation

I am using XML Documentation for my ASP.NET Web API Help Page as shown here.
I would like to know if there is a way to include html in the comments such that it will be rendered on the web page, instead of it being removed/ignored/escaped.
Specifically, I am looking for a way to create a newline, but being able to create bulleted lists, etc. would be great!
Ex/ I would like to be able to do something like this:
/// <summary>
/// CRUD operations for SalesDocument<br/>
/// This is a new line
/// </summary>
[RoutePrefix("api/SalesDocument")]
public partial class SalesDocumentController : ApiController
And have it show on the help page like this:
CRUD operations for SalesDocument
This is a new line.
Instead of this: (in this case, <br/> gets removed somehow - if I try using <p> tags, they are just escaped)
CRUD operations for SalesDocument This is a new line.
*I have already tried the <para> tag as suggested by multiple posts for tooltips, but this does not work on my help page.
Any suggestions are greatly appreciated!
In the installed XmlDocumentationProvider.cs file at Areas\HelpPage, you can look for a method called GetTagValue. Here modify the return value from node.Value.Trim() to node.InnerXml.
private static string GetTagValue(XPathNavigator parentNode, string tagName)
{
if (parentNode != null)
{
XPathNavigator node = parentNode.SelectSingleNode(tagName);
if (node != null)
{
return node.InnerXml;
}
}
return null;
}
Now open the installed file Areas\HelpPage\Views\Help\DisplayTemplates\ApiGroup.cshtml and modify the following line from:
<p>#controllerDocumentation</p>
to
<p>#Html.Raw(controllerDocumentation)</p>
#controllerDocumentation does not work for me, but changing the line to#api.Documentation works. i.e. #html.raw(api.Documentation).

MiniProfiler can't display SqlTimings

I am using MiniProfiler to Profile My MVC app and WCF services, this works like a charm with one caveat - when the profile information contains sql.
Symptoms:
The "query time (ms)" heading is missing from the popup
The "% in sql" is also missing from the bottom of the popup
If I click on the " sql" links it shows the grey overlay but no information and throws some jQuery error (it can't find the element).
After a little digging I discovered that this is all to do with HasSqlTimings in the json response there is an inconsistency between HasSqlTimings (false) at the root of the json response and the information that is in Root / Children hierarchy (true).
[OnDeserialized]
void OnDeserialized(StreamingContext ctx)
{
HasSqlTimings = GetTimingHierarchy().Any(t => t.HasSqlTimings);
HasDuplicateSqlTimings = GetTimingHierarchy().Any(t => t.HasDuplicateSqlTimings);
if (_root != null)
{
_root.RebuildParentTimings();
}
}
I took a look at the source and it looks like it should work just fine but no deal! Does anyone have any idea where I might be going wrong?
I had the same issue when implementing this. The issue occurs when the UI layer doesn't make any sql calls but then the WCF data comes back with sql calls. The root timing doesn't know there are sql timings below in the hierarchy.
I added one line so that when adding "remote" timings we set the "hasSqlTimings" field so that the UI knows how to render the box properly. Here is the code I modified in MvcMiniProfiler\MiniProfiler.cs:
/// <summary>
/// Adds <paramref name="externalProfiler"/>'s <see cref="Timing"/> hierarchy to this profiler's current Timing step,
/// allowing other threads, remote calls, etc. to be profiled and joined into this profiling session.
/// </summary>
public static void AddProfilerResults(this MiniProfiler profiler, MiniProfiler externalProfiler)
{
if (profiler == null || externalProfiler == null) return;
profiler.Head.AddChild(externalProfiler.Root);
profiler.HasSqlTimings = profiler.GetTimingHierarchy().Any(t => t.HasSqlTimings);
}

Resources