I am using Microsoft.Owin.Security.Jwt. My resource server is configured as follows:
// Resource server configuration
var audience = "hello";
var secret = TextEncodings.Base64Url.Decode("world);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
}
});
Currently, when a token is expired, the Reponse is as follows:
401 Unauthorized
**Headers:**
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/10.0
Www-Authenticate: Bearer
X-Sourcefiles: =?UTF-8?B?Yzpcc3JjXFVTQi5FbnRlcnByaXNlQXV0b21hdGlvbi5BdXRoQXBpXFVTQi5FbnRlcnByaXNlQXV0b21hdGlvbi5BdXRoQXBpXGFwaVx1c2VyXGxvb2t1cFxsaWtvc3Rv?=
X-Powered-By: ASP.NET
Date: Fri, 30 Dec 2016 13:54:26 GMT
Content-Length: 61
Body
{
"message": "Authorization has been denied for this request."
}
Is there a way to set a custom Www-Authenticate header, and/or add to the body if the token is expired?
I'd like to return something like:
WWW-Authenticate: Bearer realm="example",
error="invalid_token",
error_description="The access token expired"
One way to do this is to create a custom AuthorizeAttribute and then decorate the method or class in question. Make sure to override HandleUnauthorizedRequest and then call its base method to carry on as normal and return 401.
public class CustomAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
HttpContext.Current.Response.AppendHeader("WWW-Authenticate", #"Bearer realm=""example"" ... ");
base.HandleUnauthorizedRequest(actionContext);
}
}
Usage:
[CustomAuthorize]
public IHttpActionResult Get()
{
...
}
May need some further logic around headers but should be enough to get started with.
It looks like ServiceStack doesn't like me using a DateTime property as an argument in my request. I'm getting a "Bad Request" message... no other helpful detail in the exception. The inner exception shows html code (truncated), and just says "Type definitions should start with a "{" expecting serialized type 'ErrorResponse'..."
In my client:
private DateTime _selectedReportDate;
public DateTime SelectedReportDate
{
get { return _selectedReportDate; }
set { SetProperty(ref _selectedReportDate, value); }
}
....
var txResults = await ServiceClient.Instance.GetAsync(new PaymentSummaries()
{
Date = SelectedReportDate
});
Service Model:
[Route("/report/paymentsummaries/{Date}", "GET")]
public class PaymentSummaries : IReturn<List<PaymentSummary>>
{
public DateTime Date { get; set; }
}
Service Interface:
[Authenticate]
public class PaymentSummariesService : Service
{
public List<PaymentSummary> Get(PaymentSummaries request)
{
var results = Db.SqlList<Data.OrmLite.SpResponse.ReconcilePaymentSummaryRecord>("EXEC [Report].[ReconcilePaymentsSummary] #date", new { date = request.Date });
return results.ConvertAll(x => x.ConvertTo<PaymentSummary>());
}
}
I'm getting a "Bad Request" error.
When I change:
Date = SelectedReportDate
to
Date = new DateTime()
in the client code, it does work, and hits the Service Interface code for some reason.
Update
Here's the request header:
GET
http://devservicestack:44345/report/paymentsummaries/2016-11-30T13%3A09%3A15.6795974-05%3A00
HTTP/1.1 Accept-Encoding: gzip,deflate Accept: application/json
User-Agent: ServiceStack .NET Client 4.54 Host: devservicestack:44345
Cookie: ss-id=F4Bt4aMonhyFQcfqmSmR; ss-pid=K6aJMA17Xw31qIVy1z8V;
ss-opt=temp
The response header tells me:
[HttpException (0x80004005): A potentially dangerous Request.Path
value was detected from the client (:).]
System.Web.HttpRequest.ValidateInputIfRequiredByConfig() +9827624
System.Web.PipelineStepManager.ValidateHelper(HttpContext context) +53
When hosting ServiceStack in ASP.Net (as opposed to self-hosting), ASP.Net utilizes XSS security checks. to get around this, I can allow specific characters:
<system.web>
<httpRuntime targetFramework="4.6.2" requestPathInvalidCharacters="<,>,*,%,&,\,?" />
</system.web>
(omitted ":" in requestPathInvalidCharacters)
or:
<system.web>
<httpRuntime targetFramework="4.6.2" requestValidationMode="2.0" requestPathInvalidCharacters="" />
</system.web>
to disable request validation for the entire application.
Why does this simple web service refuse to return JSON to the client?
Here is my client code:
var params = { };
$.ajax({
url: "/Services/SessionServices.asmx/HelloWorld",
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
timeout: 10000,
data: JSON.stringify(params),
success: function (response) {
console.log(response);
}
});
And the service:
namespace myproject.frontend.Services
{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[ScriptService]
public class SessionServices : System.Web.Services.WebService
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string HelloWorld()
{
return "Hello World";
}
}
}
web.config:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
</configuration>
And the response:
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://tempuri.org/">Hello World</string>
No matter what I do, the response always comes back as XML. How do I get the web service to return Json?
EDIT:
Here is the Fiddler HTTP trace:
REQUEST
-------
POST http://myproject.local/Services/SessionServices.asmx/HelloWorld HTTP/1.1
Host: myproject.local
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0.1
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Referer: http://myproject.local/Pages/Test.aspx
Content-Length: 2
Cookie: ASP.NET_SessionId=5tvpx1ph1uiie2o1c5wzx0bz
Pragma: no-cache
Cache-Control: no-cache
{}
RESPONSE
-------
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 19 Jun 2012 16:33:40 GMT
Content-Length: 96
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://tempuri.org/">Hello World</string>
I have lost count of how many articles I have read now trying to fix this. The instructions are either incomplete or do not solve my issue for some reason.
Some of the more relevant ones include (all without success):
ASP.NET web service erroneously returns XML instead of JSON
asmx web service returning xml instead of json in .net 4.0
http://williamsportwebdeveloper.com/cgi/wp/?p=494
http://encosia.com/using-jquery-to-consume-aspnet-json-web-services/
http://forums.asp.net/t/1054378.aspx
http://jqueryplugins.info/2012/02/asp-net-web-service-returning-xml-instead-of-json/
Plus several other general articles.
Finally figured it out.
The app code is correct as posted. The problem is with the configuration. The correct web.config is:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.webServer>
<handlers>
<add name="ScriptHandlerFactory"
verb="*" path="*.asmx"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
resourceType="Unspecified" />
</handlers>
</system.webServer>
</configuration>
According to the docs, registering the handler should be unnecessary from .NET 4 upwards as it has been moved to the machine.config. For whatever reason, this isn't working for me. But adding the registration to the web.config for my app resolved the problem.
A lot of the articles on this problem instruct to add the handler to the <system.web> section. This does NOT work and causes a whole load of other problems. I tried adding the handler to both sections and this generates a set of other migration errors which completely misdirected my troubleshooting.
In case it helps anyone else, if I had ther same problem again, here is the checklist I would review:
Did you specify type: "POST" in the ajax request?
Did you specify contentType: "application/json; charset=utf-8" in the ajax request?
Did you specify dataType: "json"in the ajax request?
Does your .asmx web service include the [ScriptService] attribute?
Does your web method include the [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
attribute? (My code works even without this attribute, but a lot of articles say that it is required)
Have you added the ScriptHandlerFactory to the web.config file in <system.webServer><handlers>?
Have you removed all handlers from the the web.config file in in <system.web><httpHandlers>?
Hope this helps anyone with the same problem. and thanks to posters for suggestions.
No success with above solution, here how I resolved it.
put this line into your webservice and rather return type just write the string in response context
this.Context.Response.ContentType = "application/json; charset=utf-8";
this.Context.Response.Write(serial.Serialize(city));
If you want to stay remain with Framework 3.5, you need to make change in code as follows.
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[ScriptService]
public class WebService : System.Web.Services.WebService
{
public WebService()
{
}
[WebMethod]
public void HelloWorld() // It's IMP to keep return type void.
{
string strResult = "Hello World";
object objResultD = new { d = strResult }; // To make result similarly like ASP.Net Web Service in JSON form. You can skip if it's not needed in this form.
System.Web.Script.Serialization.JavaScriptSerializer ser = new System.Web.Script.Serialization.JavaScriptSerializer();
string strResponse = ser.Serialize(objResultD);
string strCallback = Context.Request.QueryString["callback"]; // Get callback method name. e.g. jQuery17019982320107502116_1378635607531
strResponse = strCallback + "(" + strResponse + ")"; // e.g. jQuery17019982320107502116_1378635607531(....)
Context.Response.Clear();
Context.Response.ContentType = "application/json";
Context.Response.AddHeader("content-length", strResponse.Length.ToString());
Context.Response.Flush();
Context.Response.Write(strResponse);
}
}
There is much easier way to return a pure string from web service. I call it CROW function (makes it easy to remember).
[WebMethod]
public void Test()
{
Context.Response.Output.Write("and that's how it's done");
}
As you can see, return type is "void", but CROW function will still return the value you want.
I have a .asmx web service (.NET 4.0) with a method that returns a string. The string is a serialized List like you see in many of the examples. This will return json that is not wrapped in XML. No changes to web.config or need for 3rd party DLLs.
var tmsd = new List<TmsData>();
foreach (DataRow dr in dt.Rows)
{
m_firstname = dr["FirstName"].ToString();
m_lastname = dr["LastName"].ToString();
tmsd.Add(new TmsData() { FirstName = m_firstname, LastName = m_lastname} );
}
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
string m_json = serializer.Serialize(tmsd);
return m_json;
The client part that uses the service looks like this:
$.ajax({
type: 'POST',
contentType: "application/json; charset=utf-8",
dataType: 'json',
url: 'http://localhost:54253/TmsWebService.asmx/GetTombstoneDataJson',
data: "{'ObjectNumber':'105.1996'}",
success: function (data) {
alert(data.d);
},
error: function (a) {
alert(a.responseText);
}
});
Hope this helps, it appears that you still have to send some JSON object in the request, even if the Method you are calling has no parameters.
var params = {};
return $http({
method: 'POST',
async: false,
url: 'service.asmx/ParameterlessMethod',
data: JSON.stringify(params),
contentType: 'application/json; charset=utf-8',
dataType: 'json'
}).then(function (response) {
var robj = JSON.parse(response.data.d);
return robj;
});
For me it works with this code I got from this post:
How can I return json from my WCF rest service (.NET 4), using Json.Net, without it being a string, wrapped in quotes?
[WebInvoke(UriTemplate = "HelloWorld", Method = "GET"), OperationContract]
public Message HelloWorld()
{
string jsonResponse = //Get JSON string here
return WebOperationContext.Current.CreateTextResponse(jsonResponse, "application/json; charset=utf-8", Encoding.UTF8);
}
I have tried all of the above steps ( even the answer), but i was not successful, my system configuration is Windows Server 2012 R2, IIS 8. The following step solved my problem.
Changed the app pool, that has managed pipeline = classic.
I know that is really old question but i came to same problem today and I've been searching everywhere to find the answer but with no result. After long research I have found the way to make this work. To return JSON from service you have provide data in request in the correct format, use JSON.stringify() to parse the data before request and don't forget about contentType: "application/json; charset=utf-8", using this should provide expected result.
response = await client.GetAsync(RequestUrl, HttpCompletionOption.ResponseContentRead);
if (response.IsSuccessStatusCode)
{
_data = await response.Content.ReadAsStringAsync();
try
{
XmlDocument _doc = new XmlDocument();
_doc.LoadXml(_data);
return Request.CreateResponse(HttpStatusCode.OK, JObject.Parse(_doc.InnerText));
}
catch (Exception jex)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, jex.Message);
}
}
else
return Task.FromResult<HttpResponseMessage>(Request.CreateResponse(HttpStatusCode.NotFound)).Result;
I'd like to have an ApiController with a controller action that accepts POST requests and has a name that starts with Delete. I'm using the following jQuery:
function DeleteMyDomainObjectButton_OnClick() {
var $div = $(this).closest("div.domain-object");
var url = baseUrl + "Api/MyBusiness/DeleteMyDomainObject";
var dto = {
"MyDomainObjectId": $div.find("input[name=MyDomainObjectId]").val()
};
$.ajax({
complete: DeleteMyDomainObjectAjax_OnComplete
, dataType: "json"
, contentType: "application/json"
, accept: "applcation/json"
, data: JSON.stringify(dto)
, error: DeleteMyDomainObjectAjax_OnError
, method: "POST"
, success: DeleteMyDomainObjectAjax_OnSuccess
, url: url
});
}
I keep getting a 405: {"Message":"The requested resource does not support http method 'POST'."}. The response headers are:
HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Allow: DELETE
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 07 Jul 2015 20:45:35 GMT
Content-Length: 73
I did check the request headers, and it actually is sending a POST. Here's my controller:
public class BaseApiController : ApiController
{
/* Properties for All API Controllers set by DI */
}
public class MyBusinessController : BaseApiController
{
public virtual MyDomainObject FooBar(DeleteMyDomainObjectDto dto)
{
return this.DeleteMyDomainObject(p_input);
}
[HttpPost] // Doesn't work.
// [AcceptVerbs(HttpVerbs.Post)] // Also doesn't work.
public virtual MyDomainObject DeleteMyDomainObject(DeleteMyDomainObjectDto dto)
{
return BusinessLogic.DeleteMyDomainObject(dto)
}
}
Now, if I change the controller action that I reference from jQuery to FooBar, it works.
Question: How can I turn off the magic sauce that makes the ApiController only allow DELETE requests for controller actions with names starting with the string Delete? Or am I doing something else wrong?
This used to work, however I recently discovered that ASP.NET is not caching user roles in the cookie anymore. I ran a fiddler trace and it appears that the value for the cookie is blank and the expiration date is set in the past. Therefore the cookie is not sent on the subsequent request and the DB is hit every round trip.
I can't seem to find any posts on this. Any help would be great. Thanks!
web.config:
<roleManager enabled="true" defaultProvider="MyRoleProvider" cacheRolesInCookie="true" cookieName=".ASPXROLES" cookieTimeout="30" cookiePath="/" cookieRequireSSL="false" cookieSlidingExpiration="true" cookieProtection="All" createPersistentCookie="false">
<providers>
<clear />
<add name="MyRoleProvider" type="MyCompany.Core.Web.Providers.MyRoleProvider" connectionStringName="MainConnect" applicationName="MyApplication" />
</providers>
</roleManager>
Fiddler Response (Header):
HTTP/1.1 200 OK
Cache-Control: private, s-maxage=0
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
Set-Cookie: .ASPXROLES=; expires=Tue, 12-Oct-1999 05:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Mon, 31 Dec 2012 01:14:19 GMT
Content-Length: 1381
Take a look at This answer. It seems to indicate that only the IsUserInRole member of the provider will cache results this way. When checking user roles, ASP .NET MVC seems to use GetRolesForUser exclusively. I bumped into this same limitation not too long ago--here's some code I added to my role provider to provide a simple caching mechanism.
public class MyRoleProvider : RoleProvider
{
private readonly string userRoleCacheKeyFormat;
public MyRoleProvider()
{
userRoleCacheKeyFormat = this.Name + "_{0}";
}
public override string[] GetRolesForUser(string username)
{
return GetUserRoles(username);
}
private string[] GetUserRoles(string username)
{
string[] roleNames = null;
if (!TryGetCachedUserRoles(username, out roleNames))
{
//cache miss
roleNames = GetUserRolesFromStore(username);
}
return roleNames;
}
private bool TryGetCachedUserRoles(string username, out string[] userRoles)
{
string cacheKey = string.Format(userRoleCacheKeyFormat, username);
HttpContext httpContext = HttpContext.Current;
if (httpContext != null)
{
userRoles = (string[])httpContext.Cache.Get(cacheKey);
}
else { userRoles = null; }
return (userRoles != null);
}
private void CacheUserRoles(string username, string[] userRoles)
{
string cacheKey = string.Format(userRoleCacheKeyFormat, username);
HttpContext httpContext = HttpContext.Current;
if (httpContext != null)
{
httpContext.Cache.Insert(cacheKey, userRoles, null, DateTime.UtcNow.AddMinutes(15), Cache.NoSlidingExpiration);
}
}
private string[] GetUserRolesFromStore(string username)
{
MyDbContext db = MvcApplication.IoC.Resolve<MyDbContext>();
string[] roleNames = db.Users
.Single(u => u.Username == username)
.UserRoles
.Select(r => r.Name)
.ToArray();
CacheUserRoles(username, roleNames);
return roleNames;
}
}
I think. That Session doesn't have any Role.
try createPersistentCookie="true"