How to wire up simple Web API controller in global.asax - asp.net

I have a simple Web API (sitting inside webforms site) that generates a simple "Tip of the Day". The code is as follows:
AppCode\TipOfTheDayController.vb
Imports System.Net
Imports System.Web.Http
Public Class TipOfTheDayController
Inherits ApiController
Private Function GenerateTip() As TipOfTheDay
Dim tipCol As New List(Of Tip)
tipCol.Add(New Tip("Tip Text 01"))
tipCol.Add(New Tip("Tip Text 02"))
tipCol.Add(New Tip("Tip Text 03"))
Dim rnd As New Random
Dim i As Int16 = rnd.Next(0, tipCol.Count - 1)
Dim td As New TipOfTheDay
td.TipString = tipCol(i).TipString
td.TipNumber = i
Return td
End Function
Public Function GetTip() As TipOfTheDay
Return GenerateTip()
End Function
End Class
Public Class Tip
Public Property TipString As String
Public Sub New(ts As String)
Me.TipString = ts
End Sub
End Class
Public Class TipOfTheDay
Public Property TipString As String
Public Property TipNumber As String
End Class
I am trying to wire this up so that it can be called using
http://www.mysite.com/api/tips
Assuming the above URL is okay, I cannot figure this bit out in Global.asax. I've seen loads of examples online but all have an optional "ID" value which I don't need. Can anyone please show me what I need to do to retrieve my random "tip" from the API?
<%# Application Language="VB" %>
<%# Import Namespace="System.Web.Routing" %>
<%# Import Namespace="System.Web.Optimization" %>
<%# Import Namespace="System.Web.Http" %>
<script runat="server">
Sub RegisterRoutes(routes As RouteCollection)
routes.MapHttpRoute("TipOfTheDay", "api/tips")
End Sub
</script>

The routing is looking for an Index Action on your controller if the action was not specified in the route or by entering into the url. Rename your GetTip function to Index.
If that is not acceptable, you can add a route similar to the following in lieu of your current route.
routes.MapHttpRoute("TipOfTheDay", "api/tips", new { Controller = "TipOfTheDay" Action = "GetTip" });
I wouldn't recommend this route, however, since it will try to use GetTip as the default action every time one is not specified.
Here is a good resource for routing in a web forms application:
http://msdn.microsoft.com/en-us/library/dd329551.ASPX

Related

Why is my ASP.NET MVC View page returning 404?

I am writing a website for a friend where she can show off her goats.She would like the main site to function as a blog, and then include a database for viewers to look at the goats she owns. I started with the ASP.NET "Blank" web application template in Visual Studio 2013, with support checked for web forms and MVC. I downloaded and configured BlogEngine.NET, and copied it into the project, choosing "include in project" from solution explorer. The blog part works perfectly.
Now, I would like to add the database part. I configured connection strings in web.config, then added a Does class to the Model folder. It looks something like this:
Imports System.Data.Entity
Imports System.ComponentModel.DataAnnotations
Public Class Doe
'The registration number contains letters
<Key>
<Display(Name:="Registration #")>
Public Property RegistryNumber As String
<Required>
<Display(Name:="Name")>
Public Property FullName As String
<Required>
<DataType(DataType.Date)>
<Display(Name:="Birthday")>
<DisplayFormat(DataFormatString:="{0:MMMM dd, yyyy}")>
Public Property BirthDate As Date
Public ReadOnly Property PedigreeLink() As String
Get
Return "http://adgagenetics.org/GoatDetail.aspx?RegNumber=" & RegistryNumber.ToString()
End Get
End Property
End Class
Public Class DoeDbContext
Inherits DbContext
Public Property Does As DbSet(Of Doe)
End Class
After writing the Doe model, I right-clicked the Controllers folder, clicked Add new controller, and created an MVC 5 Controller with Views, using Entity Framework. I have not in any way modified the controller or the views from the way they were initially set up.
Edit This is the full code in my App_Start\RouteConfig.vb
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.Mvc
Imports System.Web.Routing
Public Module RouteConfig
Public Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
routes.MapRoute(
name:="Default",
url:="{controller}/{action}/{id}",
defaults:=New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional}
)
routes.MapRoute(name:="Shorthand", url:="{controller}/{action}/{name}/{id}")
End Sub
End Module
However, when I attempt to debug the application and type https://localhost:xxxx/does/index into the address bar, I receive the following 404 error:
I have added the following code to my web.config per the advice here:
<security>
<requestFiltering>
<fileExtensions>
<remove fileExtension=".cshtml" />
<add fileExtension=".cshtml" allowed="true" />
<remove fileExtension=".vbhtml" />
<add fileExtension=".vbhtml" allowed="true" />
</fileExtensions>
</requestFiltering>
</security>
Copying code out of an MVC that worked, I also added
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
I have also tried using Phil Haack's routedebugger to troubleshoot, but receive a Server Error in '/' Application. Sequence contains no elements error when trying to access any pages within the application after installing the package.
How can I fix this so I can keep the blog portion, and allow the MVC parts to work?
Edit 2 Here is my controller
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Data.Entity
Imports System.Linq
Imports System.Net
Imports System.Web
Imports System.Web.Mvc
Imports MyProjectName_Database
Namespace Controllers
Public Class DoesController
Inherits System.Web.Mvc.Controller
Private db As New DoeDbContext
' GET: Does
Function Index() As ActionResult
Return View(db.Does.ToList())
End Function
' GET: Does/Details/5
Function Details(ByVal id As String) As ActionResult
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim doe As Doe = db.Does.Find(id)
If IsNothing(doe) Then
Return HttpNotFound()
End If
Return View(doe)
End Function
' GET: Does/Create
Function Create() As ActionResult
Return View()
End Function
' POST: Does/Create
'To protect from overposting attacks, please enable the specific properties you want to bind to, for
'more details see http://go.microsoft.com/fwlink/?LinkId=317598.
<HttpPost()>
<ValidateAntiForgeryToken()>
Function Create(<Bind(Include:="RegistryNumber,FullName,BirthDate")> ByVal doe As Doe) As ActionResult
If ModelState.IsValid Then
db.Does.Add(doe)
db.SaveChanges()
Return RedirectToAction("Index")
End If
Return View(doe)
End Function
' GET: Does/Edit/5
Function Edit(ByVal id As String) As ActionResult
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim doe As Doe = db.Does.Find(id)
If IsNothing(doe) Then
Return HttpNotFound()
End If
Return View(doe)
End Function
' POST: Does/Edit/5
'To protect from overposting attacks, please enable the specific properties you want to bind to, for
'more details see http://go.microsoft.com/fwlink/?LinkId=317598.
<HttpPost()>
<ValidateAntiForgeryToken()>
Function Edit(<Bind(Include:="RegistryNumber,FullName,BirthDate")> ByVal doe As Doe) As ActionResult
If ModelState.IsValid Then
db.Entry(doe).State = EntityState.Modified
db.SaveChanges()
Return RedirectToAction("Index")
End If
Return View(doe)
End Function
' GET: Does/Delete/5
Function Delete(ByVal id As String) As ActionResult
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim doe As Doe = db.Does.Find(id)
If IsNothing(doe) Then
Return HttpNotFound()
End If
Return View(doe)
End Function
' POST: Does/Delete/5
<HttpPost()>
<ActionName("Delete")>
<ValidateAntiForgeryToken()>
Function DeleteConfirmed(ByVal id As String) As ActionResult
Dim doe As Doe = db.Does.Find(id)
db.Does.Remove(doe)
db.SaveChanges()
Return RedirectToAction("Index")
End Function
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If (disposing) Then
db.Dispose()
End If
MyBase.Dispose(disposing)
End Sub
End Class
End Namespace

Grabbing a property from an aspx page to an ascx toolbar in VB

I have a web page (aspx)- Purchasing page, with a ascx toolbar - Export Toolbar, that is used to export the data (either .xls or .csv).
I need to grab the Name of the Supplier from the Purchasing page and insert that value into the name of the export file on the ascx toolbar.
On the Purchasing page there is a ddl where the user can select the supplier and a grid that will display all the data. Above the grid there is the tool bar with an export button. I need to be able to grab the text of the dropdown list and utilize that on the ExportToolbar.ascx.vb page so I can take that text and insert it into the name.
I was trying to use a public property get and set method but it was not working. How would I go about grabbing that selected text from the Supplier ddl?
Conventional thinking goes like this: an ascx can be hosted on any aspx page. So usually it is bad form for an ascx to access properties of its host page. It is much more proper for the ascx to have a public property and the aspx will push the value into the ascx (as needed).
However, if you really want to go this route, the .Page property (of the ascx) referrs to the host page. If you cast it to the (stronger) type(name) of the host, you can get to the hosts properties. Like this:
'if your host page is called HostPage (and the class name is the same)
Dim host as HostPage = CType(me.Page, HostPage)
'now refer to the controls on the host (aspx) page
dim example as string
example = host.txtExample.Text
Keep in mind, this will cause errors if your ascx is hosted on several pages.
You can use an event form this purpose. Define the event on the UserControl like this:
Public BeforeExportEventArgs
Inherits EventArgs
Public Property FileName As String
End Class
Public Class ToolbarControl
Inherits UserControl
Public Event BeforeExport As EventHandler(Of BeforeExportEventArgs)
Public Sub btnExport_Click(sender As Object, e As EventArgs) Handles btnExport.Click
' Retrieve File Name
Dim beforeExpEventArgs As New BeforeExportEventArgs()
RaiseEvent BeforeExport(Me, beforeExpEventArgs)
' Set default filename if not provided by an event handler
If String.IsNullOrEmpty(beforeExpEventArgs.FileName) Then
beforeExpEventArgs.FileName = "DefaultFileName.csv"
End If
' Export data
End Class
Add an event handler to the form that hosts the UserControl:
Public Class WebForm1
Inherits Page
' ...
Public Sub expToolbar_BeforeExport(sender As Object, e As BeforeExportEventArgs) Handles expToolbar.BeforeExport
e.FileName = ddlSupplier.Text + ".csv"
End Sub
' ...
End Class
This way, you avoid tight coupling between the UserControl and the Page. The pages that host the UserControl can set a specific filename, but don't have to.
What I ended up doing was this-
On the ascx page I created a public property-
Public Property SupplierSelection As String
Get
Return Convert.ToString(ViewState.Item("SupplierSelection"))
End Get
Set(ByVal value As String)
ViewState.Add("SupplierSelection", value)
End Set
End Property
And then on the aspx page I added this on the load grid event-
SupergridToolbar1.SupplierSelection = ddlStrategy.SelectedItem.Text.ToString()
I was then able to use the Supplier Selection on the ascx page. Thanks for the help!

.net routing: Possible to restrict route to a certain domain?

I inherited a vb.net WebForms project that handles a couple domains. However, I want unique routes for each domain. Is there a way to do this by getting the domain when I use MapPageRoute? Or will I need to do something like:
routes.MapPageRoute("r1", "example1/page1", "~/example1/default.aspx")
routes.MapPageRoute("r2", "example2/page1", "~/example2/default.aspx")
But then the urls will need to be like:
//example1.com/example1/page1 and //example2.com/example2/page1
At Application_Start, I'd like to constrain a route to a specific domain, if possible.
* EDIT *
Ok, it looks like I was able to semi-resolve this by creating unique route names for similar route paths:
routes.MapPageRoute("r1", "page1", "~/example1/default.aspx")
routes.MapPageRoute("r2", "page1", "~/example2/default.aspx")
Then in my markup I can do:
<asp:HyperLink NavigateUrl="<%$RouteUrl:routename=r1%>" ID="link_home" runat="server">Home</asp:HyperLink>
Then in my Default page (or its master page), I can then handle the "//example.com/" request by redirecting to the respective route based on the domain.
However I'm not sure how to handle incoming requests like:
//example1.com/page1 and //example2.com/page1. I assume the first route will load for either domain. Any ideas what I can do?
Follow up to my comment:
You can instead create a constraint based on the domain. You'll need to subclass the IRouteConstraint interface.
Where you define your routes:
Dim domain1Constraint As New HostConstraint("domain1.com")
routes.MapPageRoute("r1", "page1", "~/example1/default.aspx", False, Nothing, New RouteValueDictionary(New With {domain1Constraint }))
Then create a class HostConstraint:
Imports System
Imports System.Web.Routing
Public Class HostConstraint
Implements IRouteConstraint
Private _host As String
Public Sub New(ByVal host As String)
_host = host.ToLower()
End Sub
Public Function Match(ByVal httpContext As HttpContextBase, _
ByVal route As Route, _
ByVal parameterName As String, _
ByVal values As RouteValueDictionary, _
ByVal routeDirection As RouteDirection) As Boolean Implements IRouteConstraint.Match
Dim host As String = httpContext.Request.Url.Host.ToLower()
If host.Contains(_host) Then
Return True
Else
Return False
End If
End Function
End Class

ASP.Net User Control public function is not a member

I have a user control on a Web Site with this inside.
Namespace MenuTreePanel
Public Class MenuTreePanel
Inherits System.Web.UI.UserControl
Public root As New MenuNode(0, 0, "root", "")
Public WithEvents Spany1 As HtmlGenericControl = New HtmlGenericControl("UL")
Public WithEvents Spany2 As HtmlGenericControl = New HtmlGenericControl("UL")
Public WithEvents Spany3 As HtmlGenericControl = New HtmlGenericControl("UL")
Public Function getRoot() As MenuNode
Return root
End Function
End Class
End Namespace
When I go to access the getRoot function I get Error
'getRoot' is not a member of 'ASP.MenuTreePanel'.
The namespace is incorrectly labelled as ASP, and I was wondering where that might be coming from. In the object explorer, my control is listed under both the correct namespace and the ASP namespace.
Referenced on the page using
<%# Register TagPrefix="MenuTreePanel" Src="~/MenuTreePanel.ascx" TagName="MenuTree" %>
<MenuTreePanel:MenuTree ID="menuTreeSelect" runat="server"></MenuTreePanel:MenuTree>
Edit 2:
<%# Control Language="vb" CodeBehind="~/MenuTreePanel.ascx.vb"className="MenuTreePanel" %>
and the attempt to access it
Dim root As New MenuNode(0, 0, "root", "")
root = (menuTreeSelect).getRoot()
The problem is likely that you're attempting to access the property statically. My assumption is that you do not want to access it statically, since it's a control.
My suggestion is that you look at how you're using the MenuTreePanel object.
You should be accessing it like this:
menuTreeSelect.getRoot();
and not like this:
MenuTreePanel.getRoot();
Try:
Public Shared Function getRoot() As MenuNode
Return root
End Function
I wasn't linking the CodeFile and the ASCX correctly with a Web Site.
I had to change CodeBehind to CodeFile and add an inherits, and now everything is working correctly.
Thanks for your help.

Getting a ScriptReference from a ScriptResourceMapping definition in a custom control

I am building a custom control with client side scripts that I would like to reference using ScriptManager.ScriptResourceMapping (to make use of the Path and DebugPath attributes).
I would like the custom control to be easily ported to other projects - i.e. I would like to drag and drop the codebehind files (and eventually make the control a separate DLL, but for now the drag and drop will suffice). I would therefore like to avoid (1) having the client script as an embedded resource, (2) referenced as a WebResource in the AssemblyInfo, or (3) have the ScriptManager.ScriptResourceMapping.AddDefinition in global.asax.
In simple terms can I get the script management code to be in just the custom control's code?
At the moment I am getting an error stating that the script reference cannot be found in the assembly, and I guess I am setting the wrong assembly.
My custom control code is as follows:
Public Class MyControl
Inherits System.Web.UI.LiteralControl
Implements ISectionControl, IScriptControl
Private _scriptReference As ScriptReference
Public Sub New()
' Add the resource mapping
ScriptManager.ScriptResourceMapping.AddDefinition("MyControlScript", New ScriptResourceDefinition With {
.ResourceAssembly = System.Reflection.Assembly.GetExecutingAssembly,
.ResourceName = "MyControlScript.js",
.Path = "Path.To.MyControlScript.minimised.js",
.DebugPath = "Path.To.MyControlScript.original.js"
})
' Set the script reference
_scriptReference = New ScriptReference("MyControlScript.js", Assembly.GetExecutingAssembly.FullName)
End Sub
Protected Overrides Sub OnPreRender(e As System.EventArgs)
MyBase.OnPreRender(e)
' Register the script
ScriptManager.GetCurrent(Page).RegisterScriptControl(Of MyControl)(Me)
' Some code to set the Text of the literal control
' ...
End Sub
Public Function GetScriptDescriptors() As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptDescriptor) Implements System.Web.UI.IScriptControl.GetScriptDescriptors
Return New ScriptDescriptor() {}
End Function
Public Function GetScriptReferences() As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptReference) Implements System.Web.UI.IScriptControl.GetScriptReferences
Return New ScriptReference() {_scriptReference}
End Function
End Class
I hope the question makes sense. Thanks for taking the time to read through.
Ali
Answered this myself, I was getting confused with the assemblies and the constructors for ScriptReference. I just wanted a ScriptReference with the (mapped) name so I used the blank constructor and then set Name. I could then remove the assembly information.
Adjusting the following sorted the problem out:
Public Sub New()
' Add the resource mapping
ScriptManager.ScriptResourceMapping.AddDefinition("MyControlScript", New ScriptResourceDefinition With {
.Path = "Path.To.MyControlScript.minimised.js",
.DebugPath = "Path.To.MyControlScript.original.js"
})
' Set the script reference
_scriptReference = New ScriptReference() With {.Name="MyControlScript"}
End Sub

Resources