WebApi PUT does not work when the method has variables MVC 4 - asp.net-mvc-4

I'm new to WebApi and the tutorial was going well. I got Get, Get with variables, Post, and Delete to work, but I can't seem to get Put to work. Well, if I remove all the variables from the method the in the controller, it actually goes into the method, but that's pretty useless. Has anyone come across this before that knows how to fix it?
PUT Url sent to Postman:
http://localhost:60679/api/incident/1234
Preview:
PUT /api/incident/1234 HTTP/1.1
Host: localhost:60679
Cache-Control: no-cache
Error Message:
{"Message":"No HTTP resource was found that matches the request URI
'http://localhost:60679/api/incident/1234'.","MessageDetail":"No action was found on the
controller 'Incident' that matches the request."}
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebApiTest.AppLogic;
using WebApiTest.Data.Core;
namespace WebApiTest.WebApi.Controllers
{
public class IncidentController : ApiController
{
// GET api/values
public IEnumerable<Incident> Get()
{
using (EntityDemoEntities context = new EntityDemoEntities())
{
return context.Incidents.ToList<Incident>();
}
}
// GET api/values/5
public Incident Get(string id)
{
using (EntityDemoEntities context = new EntityDemoEntities())
{
Guid tempGuid = new Guid(id);
return context.Incidents.SingleOrDefault(i => i.IncidentId == tempGuid);
}
}
// PUT api/values/5
[HttpPut]
public void Put(string guid)
{
HttpResponseMessage result = new HttpResponseMessage();
try
{
if (ModelState.IsValid)
{
//Do something
}
else
{
result = Request.CreateResponse(HttpStatusCode.InternalServerError, "Invalid Model");
}
}
catch (Exception ex)
{
result = Request.CreateResponse(HttpStatusCode.InternalServerError, String.Format("{1}{0}{2}{0}{3}{0}{4}", Environment.NewLine, ex.Message, ex.StackTrace, ex.InnerException == null ? String.Empty : ex.InnerException.Message, ex.InnerException == null ? String.Empty : ex.InnerException.StackTrace));
}
}
}
}
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Web.config
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name ="WebDAVModule"/>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<modules runAllManagedModulesForAllRequests="true">
<remove name='WebDAVModule'/>
</modules>

Change your action to Put(string id) or Put([FromUri(Name="id")]string guid)

Related

404 (Not Found) for PUT and EDIT after enabling Cors

I'm using OWIN authentication and testing my Web APIs locally and I got some errors about CORS. I solved the problem just by adding customeHeaders tag in web.config. Now I'm able to login and and also get data. But when I try to PUT or DELETE requests, I get 404 (Not Found) error.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
var jsonFormatter = new JsonMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
}
}
and in web.config
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="*" />
<add name="Access-Control-Allow-Headers" value="*" />
</customHeaders>
</httpProtocol>

call service from code behind & script (same/different domain)

Can someone give an example of a WCF service in C#, that can be called both from code behind, client script (same domain & different domain)
I will give example of a simple service that will have 2 operation contracts.
My service contract
using System.ServiceModel;
using System.ServiceModel.Web;
namespace Sum_WcfService
{
[ServiceContract(Namespace = "JsonpAjaxService")]
public interface IService1
{
// Default method for WebInvoke is "POST", cross domain requests are made on GET method
// I will Invoke this operation on post request
[OperationContract]
[WebInvoke(ResponseFormat = WebMessageFormat.Json)]
int AddNums(int Num1, int Num2);
// I will Invoke this operation on get request, from different domain
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
string ReturnNum(int Num);
}
}
Service Implementation
using System.ServiceModel.Activation;
namespace Sum_WcfService
{
[AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1 : IService1
{
public int AddNums(int Num1, int Num2)
{
return Num1 + Num2;
}
public string ReturnNum(int Num)
{
return "Hey, You called ReturnNum with:" + Num;
}
}
}
To make the service callable from different domain, add Factory property as follows:
<%# ServiceHost Language="C#" Debug="true" Service="Sum_WcfService.Service1" CodeBehind="Service1.svc.cs" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>
Now Config of the project having service
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
<!--To make service callable from code behind & script as well-->
<client>
<endpoint address="http://localhost/Sum_Wcf/Service1.svc" binding="webHttpBinding"
behaviorConfiguration="EndPointBehavior" contract="SumServiceReference.IService1" name="WebHttpBinding_Well" />
</client>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/>
<!--Calling from different domain -->
<standardEndpoints>
<webScriptEndpoint>
<standardEndpoint name="" crossDomainScriptAccessEnabled="true">
</standardEndpoint>
</webScriptEndpoint>
</standardEndpoints>
<behaviors>
<endpointBehaviors>
<behavior name="EndPointBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Now add an empty webform to your project, to test your service
add following code in aspx page, include a jquery file.
<script type="text/javascript">
$(document).ready(
var Num1 = 10;
var Num2 = 20;
$.ajax({
type: "POST",
url: 'Service1.svc/AddNums',
contentType: "application/json; charset=utf-8",
data: '{"Num1": "' + Num1 +'"' +',' + '"Num2": "' + Num2 + '"}',
dataType: "json",
processData: false,
success: function (data) {
alert("success:" + data.d);
},
error: function (result) {
alert("error: " + result);
}
})
);
This is how you can call from same domain client script.
Now to call from code behind, you need to create proxy class of the service. Add service reference to your service.
I added service reference with name "SumServiceReference".
My code behind is as follows:
using System;
using System.Globalization;
using Sum_WcfService.SumServiceReference;
namespace Sum_WcfService
{
public partial class AddServiceClient : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var ServiceProxy = new Service1Client();
var Sum = ServiceProxy.AddNums(43, 37);
Page.ClientScript.RegisterStartupScript(GetType(), "ShowAlert", "alert(" + Sum + ")",true);
}
}
}
Now let's see how to call from cross domain script. Create a new project add a webform & to it's aspx page add following code:
<script type="text/javascript">
var Num = 7;
$(document).ready(
$.ajax({
type: "GET",
url: 'http://localhost:50345/Service1.svc/ReturnNum?Num=' + Num,
dataType: "jsonp",
processdata: false,
success: function(data) {
alert("success:" + data);
},
error: function(result) {
alert("Failed to call service");
}
})
);
</script>
Now web.config of your new project(for consuming wcf service) should be:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
</configuration>
Try this code out & report if face any issue.

ASP.NET MVC 4 WebSecurity Seed

I have some simple DbContext:
public class AuthContext : DbContext
{
public AuthContext() : base("AuthContext")
{
}
public DbSet<User> Users { get; set; }
}
And simple User model:
[Table("User")]
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string Login { get; set; }
}
What I need is to seed data to WebSecurity always or after model creating. I have tried:
Database.SetInitializer(new AuthDbSeeder());
//--------------------------------------------------------
<add name="AuthContext" connectionString="Data Source=.\SQLEXPRESS; Initial Catalog=ChatAuth; Integrated Security=SSPI; MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
and in <system.web> I added:
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<clear/>
<add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<clear/>
<add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
</providers>
</membership>
//--------------------------------------------------------
public class AuthDbSeeder : DropCreateDatabaseAlways<AuthContext>
{
protected override void Seed(AuthContext context)
{
WebSecurity.InitializeDatabaseConnection("AuthContext", "User", "UserId", "Login",
autoCreateTables: true);
WebSecurity.CreateUserAndAccount("Sergey", "1234");
But after all I have error that Database can't be droped because it is already in use. I need some working method to seed data to WebSecurity.
Also very important for me is: how I can add my custom models in the same DbContext and seed data to this context properly.
Also any ideas how I can Unit test WebSecurity?
There is an example on how to seed and customize WebSecurity here.
I have found the solution for the seed question
I reworked SimpleMembershipInitializer:
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
try
{
WebSecurity.InitializeDatabaseConnection("AuthContext", "User", "UserId", "Login",
autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
And in my seed method call
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
LazyInitializer.EnsureInitialized(ref initializer, ref isInitialized, ref initializerLock);
}
with null as the parameter.
Unit testing is still a problem

RavenDb Management Studio Not Opening in Browser

Store initialisation:
private static IDocumentStore CreateDocumentStore()
{
var store = (DocumentStore) new EmbeddableDocumentStore
{
ConnectionStringName = "RavenDb",
};
NonAdminHttp.EnsureCanListenToWhenInNonAdminContext(8080);
store.Initialize();
store.Conventions.MaxNumberOfRequestsPerSession = 500;
return store;
}
Config:
</configSections>
<appSettings>
<add key="Raven/Port" value="8080"/>
<add key="Raven/DataDir" value="~\#App_Data"/>
<add key="Raven/AnonymousAccess" value="Get" />
</appSettings>
<connectionStrings>
<clear/>
<add name="RavenDb" connectionString="DataDir=~\#App_Data\Raven" />
</connectionStrings>
The application works and raven persists some of the data, I just can't get to the management studio
By default the studio isn't served. You have to enable RavenDB's embedded web server when constructing the store instance:
var store = (DocumentStore) new EmbeddableDocumentStore
{
ConnectionStringName = "RavenDb",
UseEmbeddedHttpServer = true
};

WCF - Host Config Problem. Behavior tag is not recognized

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="HTTPBaseAddress"
value="http://localhost:8000/Derivatives/"/>
<add key="TCPBaseAddress"
value="net.tcp://localhost:8010/Derivatives/"/>
</appSettings>
<system.diagnostics>
<sources>
<source
name="System.ServiceModel.MessageLogging"
switchValue="Verbose">
<listeners>
<add
name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="c:\logs\message.log" />
</listeners>
</source>
</sources>
<trace autoflush="true" />
</system.diagnostics>
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true"
maxMessagesToLog="300"
logMessagesAtServiceLevel="false"
logMalformedMessages="true"
logMessagesAtTransportLevel="true" />
</diagnostics>
<services>
<service type=
"DerivativesCalculator.DerivativesCalculatorServiceType,DerivativesCalculatorService"
behaviorConfiguration="DerivativesCalculatorService">
<endpoint
address="Calculator"
binding="wsHttpBinding"
contract=
"DerivativesCalculator.IDerivativesCalculator,DerivativesCalculatorService"
/>
</service>
</services>
<behaviors>
<behavior name="DerivativesCalculatorService">
<serviceSecurityAudit auditLogLocation="Application" messageAuthenticationAuditLevel="SuccessOrFailure" serviceAuthorizationAuditLevel="SuccessOrFailure"/>
</behavior>
</behaviors>
</system.serviceModel>
</configuration>
This App.config is generating the following Exception:
Unrecognized attribute 'type'. Note that attribute names are case-sensitive.
My Source code is as follows:
using System;
using System.Collections.Generic;
using System.Text;
namespace DerivativesCalculator
{
public class Calculator
{
public decimal CalculateDerivative(
string[] symbols,
decimal[] parameters,
string[] functions)
{
return (decimal)(System.DateTime.Now.Millisecond);
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using DerivativesCalculatorService;
using DerivativesCalculator;
namespace DerivativesCalculator
{
public class DerivativesCalculatorServiceType : IDerivativesCalculator
{
decimal IDerivativesCalculator.CalculateDerivative(
string[] symbols,
decimal[] parameters,
string[] functions)
{
return new Calculator().CalculateDerivative(
symbols, parameters, functions);
}
void IDerivativesCalculator.DoNothing()
{
return;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
namespace DerivativesCalculatorService
{
[ServiceContract]
public interface IDerivativesCalculator
{
[OperationContract]
decimal CalculateDerivative(
string[] symbols,
decimal[] parameters,
string[] functions);
void DoNothing();
}
}
using System;
using System.Collections.Generic;
using System.Configuration;
using System.ServiceModel;
using System.Text;
namespace DerivativesCalculator
{
public class Program
{
public static void Main(string[] args)
{
Type serviceType = typeof(DerivativesCalculatorServiceType);
string httpBaseAddress = ConfigurationManager.AppSettings["HTTPBaseAddress"];
string tcpBaseAddress = ConfigurationManager.AppSettings["TCPBaseAddress"];
Uri httpBaseAddressUri = new Uri(httpBaseAddress);
Uri tcpBaseAddressUri = new Uri(tcpBaseAddress);
Uri[] baseAdresses = new Uri[] {
httpBaseAddressUri,
tcpBaseAddressUri};
using (ServiceHost host = new ServiceHost(
serviceType,
baseAdresses))
{
host.Open();
Console.WriteLine("The derivatives calculator service is available.");
Console.ReadKey();
host.Close();
}
}
}
}
How to solve this?
I believe that this line of your configuration file is incorrect:
<service
type="DerivativesCalculator.DerivativesCalculatorServiceType,DerivativesCalculatorService"
behaviorConfiguration="DerivativesCalculatorService">
I think instead the type="" should be name="".
<service
name="DerivativesCalculator.DerivativesCalculatorServiceType,DerivativesCalculatorService"
behaviorConfiguration="DerivativesCalculatorService">