ASP.NET Web API : Correct way to return a 401/unauthorised response
You should be throwing a HttpResponseException
from your API method, not HttpException
:
throw new HttpResponseException(HttpStatusCode.Unauthorized);
Or, if you want to supply a custom message:
var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "Oops!!!" };
throw new HttpResponseException(msg);
How to return status code 401 along some additional data?
Answering your first question, this is how overridden method of authorization attribute may look like. Error message will be status message and content is in response body.
public override Task OnAuthorizationAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
{
string errorMessage = "User has no enough permissions to perform requested operation.";
var httpContent = new StringContent("{ \"some\": \"json\"}", Encoding.UTF8, "application/json");
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden)
{
ReasonPhrase = errorMessage,
Content = httpContent
};
return Task.FromResult<object>(null);
}
From MVC action you can return status code like this return StatusCode(418);
or using dedicated method like return Unauthorized();
. To redirect you can use RedirectToAction
or context.Response.Redirect
How to return Unathorized from .Net Core Web API
If the only reason you don't have a IActionResult return type is because you want to return json data, you can still return it and do this:
public IActionResult GetSomeData()
{
if (condition)
return Json(myData);
else
return Unauthorized();
}
A little hacky, but you can also simply return null and configure your response using HttpContext
public SomeData GetSomeData()
{
if (condition) return myData;
else
{
HttpContext.Response.StatusCode = 401;
return null;
}
}
If you need SomeData
for some reason such as type safety, one of your options are to set up a filter class.
public class MyAccessAttribute : Attribute, IActionFilter{
public void OnActionExecuting(ActionExecutingContext context)
{
if (condition)
context.Result = new UnauthorizedResult();
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
You can then use it on your action like this:
[MyAccess]
public SomeData GetSomeData(){
Update
As of .netcore 2.1 you can now use generic ActionResult
public ActionResult<SomeData> GetSomeData(){
Unauthorised webapi call returning login page rather than 401
There are two AuthorizeAttribute implementations and you need to make sure you are referencing the correct one for Web API's. There is System.Web.Http.AuthorizeAttribute which is used for Web API's, and System.Web.Mvc.AuthorizeAttribute which is used for controllers with views. Http.AuthorizeAttribute will return a 401 error if authorization fails and Mvc.AuthorizeAttribute will redirect to the login page.
Updated 11/26/2013
So it appears things have drastically changed with MVC 5 as Brock Allen pointed out in his article. I guess the OWIN pipeline takes over and introduces some new behavior. Now when the user is not authorized a status of 200 is returned with the following information in the HTTP header.
X-Responded-JSON: {"status":401,"headers":{"location":"http:\/\/localhost:59540\/Account\/Login?ReturnUrl=%2Fapi%2FTestBasic"}}
You could change your logic on the client side to check this information in the header to determine how to handle this, instead of looking for a 401 status on the error branch.
I tried to override this behavior in a custom AuthorizeAttribute by setting the status in the response in the OnAuthorization and HandleUnauthorizedRequest methods.
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
But this did not work. The new pipeline must grab this response later and modify it to the same response I was getting before. Throwing an HttpException did not work either as it is just changed into a 500 error status.
I tested Brock Allen's solution and it did work when I was using a jQuery ajax call. If it is not working for you my guess is that it is because you are using angular. Run your test with Fiddler and see if the following is in your header.
X-Requested-With: XMLHttpRequest
If it is not then that is the problem. I am not familiar with angular but if it lets you insert your own header values then add this to your ajax requests and it will probably start working.
asp mvc web api return 401 unauthorized
use [AllowAnonymous]
attribute on intended controller for all actions on that cotroller
[AllowAnonymous] // Applies to all ations
public class MyController : ApiController {
public IHttpActionResult MyAction1() {
//...
}
public IHttpActionResult MyAction2() {
//...
}
}
or only on the intended action
public class MyController : ApiController {
[AllowAnonymous] // Applies to only this ation
public IHttpActionResult MyAction() {
//...
}
//Still secure
public IHttpActionResult MyAction2() {
//...
}
}
When done testing you can remove them.
Related Topics
How to Iterate Through the Following Json Using C#
Returning a File to View/Download in ASP.NET MVC
Reload Page After Jquery.Get to MVC Controller Action
Why Am I Getting Error Cs0246: the Type or Namespace Name Could Not Be Found
Why Does Integer Division in C# Return an Integer and Not a Float
Asp.Net Core Get Json Array Using Iconfiguration
Count() a Specfic Attribute Within a List C#
How to Add a Run Button and Compile Button on the Toolbar in Visual Studio
Asp.Net MVC - How to Call Void Controller Method Without Leaving the View
Thread.Sleep() Without Freezing the Ui
How to Check If a Datetime Value Is Empty or Not in a Put Request
How to Ignore First Two Columns of CSV File
Json Deserializer Returning Null Values
Kill Child Process When Parent Process Is Killed
Null Value on Xml Deserialization Using [Xmlattribute]
User.Identity.Getuserid() Returns Null After Successful Login