AuthorizeAttribute at asp.net MVC cannot write log to file - asp.net

I try to customize AuthorizeAttribute for authentication at restful service. I want to write information into a log file. But it didn't work. I don't see any log file at my log path. (I have enable permission to IIS_USER)
Also, I even found that if AuthorizeCore return false, my test client can still get the result. Is somewhere in my code wrong? Or something I misunderstand how AuthorizeAttribute work?
PS. I also found that if I switch log part to ApiController it will work! It's so weird!
Filter
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
using (StreamWriter sw = new StreamWriter(#"C:\inetpub\wwwroot\mvctest\logs\Trace.log", true, Encoding.UTF8))
{
IEnumerable<String> header = httpContext.Request.Headers.GetValues("User");
foreach (String str in header)
{
sw.WriteLine(str);
}
sw.Flush();
}
return base.AuthorizeCore(httpContext);
}
}
ApiController
public class ValuesController : ApiController
{
// GET api/values
[MyAuthorizeAttribute]
public List<String> Get()
{
return new List<String> { "Success", "Get" };
}
// GET api/values/5
[MyAuthorizeAttribute]
public String Get(int id)
{
return "Success";
}
}
TestClient
static void Main(string[] args)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost/mvctest/api/values/5");
request.Headers.Add("User", "Darkurvivor");
using (WebResponse response = request.GetResponse())
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
String data = sr.ReadToEnd();
Console.WriteLine(data);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadKey();
}

There are two AuthorizeAttributes. One for WebApi and one for MVC. WebApi uses System.Web.http.AuthorizeAttribute, while MVC uses System.Web.Mvc.AuthorizeAttribute. You canoot use one with the other. So, no. It's not weird at all.

Related

HttpClientFactory HttpClient Cannot access a disposed object

I am implementing a service for posting data to an external RestAPI.
What I did as below:
Service definition:
public class ExternalOutputService : IExternalOutputService
{
private readonly HttpClient _httpClient;
public ExternalOutputService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<object> Send(object data, string baseAddress, string uri)
{
try
{
HttpResponseMessage response = await _httpClient.PostAsJsonAsync(uri, data);
response.EnsureSuccessStatusCode();
}
catch (Exception ex) {
Console.Write(ex.Message);
}
return response.Content;
}
}
Add services.AddHttpClient<IExternalOutputService, ExternalOutputService>(); in Startup
Use the injected the service and call the Send method.
public class ConfigurableOutput
{
private readonly IExternalOutputService _externalOutputService;
public ConfigurableOutput(IExternalOutputService externalOutputService)
{
_externalOutputService = externalOutputService;
}
public override async Task<object> Run(object input)
{
await _externalOutputService.Send(input.data, "URI address");
}
}
But when I run it and hit the httpclient send line, it would throw an exception with 'Cannot access a disposed object'
Anyone has idea or advice?
Hi guys, I finally find the issue.
In another DI extension class, the class has already been registered.
context.Services.AddTransient<IExternalOutputService, ExternalOutputService>();
So removed this line and only keeps
services.AddHttpClient<IExternalOutputService, ExternalOutputService>();
It is all good now.

asp.net core - pass exception description to client (e.g. Angular)

I've been trying to fetch exception text from backend (ASP.NET Core) in Angular app.
I've seen examples where the controller action's return type is JsonResult or ActionResult.
In this case we can do the following:
[HttpGet]
public ActionResult GetSomething()
{
...
}
catch (Exception ex)
{
return Json(new { error = $"{ex.GetType().FullName}: '{ex.Message}'" }, JsonRequestBehavior.AllowGet);
}
}
All controller actions I have return DTOs, e.g.
public async Task<List<OrderDto>> GetMany(long clientId)
{
....
Since I'm returning DTO - I can't seem to return Json, so the approach above doesn't work.
I wonder if there's a way to pass exception description other than via Json(...).
Does anyone have an idea of how to handle this?
You can create a middleware:
public class ExceptionHandleMiddleware
{
private readonly RequestDelegate next;
public ExceptionHandleMiddleware(RequestDelegate next)
{
this.next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
context.Response.Clear();
context.Response.ContentType = #"application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new { error = $"{ex.GetType().FullName}: '{ex.Message}'"}));
}
}
}
And then add it to application Builder in Configure method:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
....
app.UseMiddleware<ExceptionHandleMiddleware>();
}
Pay attention that with this middleware you shouldn't catch your exception on Controller level.

Return custom HTTP code from ActionFilterAttribute

I use the code below to throttle my ASP.NET Web Api:
public class Throttle : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext context, CancellationToken cancellationToken)
{
// ...
if (throttle)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Conflict));
}
}
}
However, I cannot return error code 429, because it's not in HttpStatusCode enum. Is there a way to return a custom error code?
I found this over here.
var response = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)429,
ReasonPhrase = "Too Many Requests",
Content = new StringContent(string.Format(CultureInfo.InvariantCulture, "Rate limit reached. Reset in {0} seconds.", data.ResetSeconds))
};
response.Headers.Add("Retry-After", data.ResetSeconds.ToString(CultureInfo.InvariantCulture));
actionContext.Response = response;
Hope this helps
This is what I did based on another response on StackOverflow.
Create Class (in controller file worked for me)
public class TooManyRequests : IHttpActionResult
{
public TooManyRequests()
{
}
public TooManyRequests(string message)
{
Message = message;
}
public string Message { get; private set; }
public HttpResponseMessage Execute()
{
HttpResponseMessage response = new HttpResponseMessage((HttpStatusCode)429);
if (!string.IsNullOrEmpty(Message))
{
response.Content = new StringContent(Message); // Put the message in the response body (text/plain content).
}
return response;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
}
Use in controller
public IHttpActionResult Get()
{
// with message
return new TooManyRequests("Limited to 5 request per day. Come back tomorrow.");
// without message
// return new TooManyRequests();
}

How to return a view for HttpNotFound() in ASP.Net MVC 3?

Is there a way to return the same view every time a HttpNotFoundResult is returned from a controller? How do you specify this view? I'm guessing configuring a 404 page in the web.config might work, but I wanted to know if there was a better way to handle this result.
Edit / Follow up:
I ended up using the solution found in the second answer to this question with some slight tweaks for ASP.Net MVC 3 to handle my 404s: How can I properly handle 404s in ASP.Net MVC?
HttpNotFoundResult doesn't render a view. It simply sets the status code to 404 and returns an empty result which is useful for things like AJAX but if you want a custom 404 error page you could throw new HttpException(404, "Not found") which will automatically render the configured view in web.config:
<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="/Http404.html" />
</customErrors>
This solution combines IResultFilter and IExceptionFilter to catch either thrown HttpException or returned HttpStatusCodeResult from within an action.
public class CustomViewForHttpStatusResultFilter: IResultFilter, IExceptionFilter
{
string viewName;
int statusCode;
public CustomViewForHttpStatusResultFilter(HttpStatusCodeResult prototype, string viewName)
: this(prototype.StatusCode, viewName) {
}
public CustomViewForHttpStatusResultFilter(int statusCode, string viewName) {
this.viewName = viewName;
this.statusCode = statusCode;
}
public void OnResultExecuted(ResultExecutedContext filterContext) {
HttpStatusCodeResult httpStatusCodeResult = filterContext.Result as HttpStatusCodeResult;
if (httpStatusCodeResult != null && httpStatusCodeResult.StatusCode == statusCode) {
ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
}
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
}
public void OnException(ExceptionContext filterContext) {
HttpException httpException = filterContext.Exception as HttpException;
if (httpException != null && httpException.GetHttpCode() == statusCode) {
ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
// This causes ELMAH not to log exceptions, so commented out
//filterContext.ExceptionHandled = true;
}
}
void ExecuteCustomViewResult(ControllerContext controllerContext) {
ViewResult viewResult = new ViewResult();
viewResult.ViewName = viewName;
viewResult.ViewData = controllerContext.Controller.ViewData;
viewResult.TempData = controllerContext.Controller.TempData;
viewResult.ExecuteResult(controllerContext);
controllerContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
You can register this filter so, specifying either the http status code of the HttpException or the concrete HttpStatusCodeResult for which you want to display the custom view.
GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(new HttpNotFoundResult(), "Error404"));
// alternate syntax
GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(404, "Error404"));
It handles exceptions and HttpStatusCodeResult thrown or returned within an action. It won't handle errors that occur before MVC selects a suitable action and controller like this common problems:
Unknown routes
Unknown controllers
Unknown actions
For handling these types of NotFound errors, combine this solution with other solutions to be found in stackoverflow.
Useful info from #Darin Dimitrov that HttpNotFoundResult is actually returning empty result.
After some study. The workaround for MVC 3 here is to derive all HttpNotFoundResult, HttpUnauthorizedResult, HttpStatusCodeResult classes and implement new (overriding it) HttpNotFound() method in BaseController.
It is best practise to use base Controller so you have 'control' over all derived Controllers.
I create new HttpStatusCodeResult class, not to derive from ActionResult but from ViewResult to render the view or any View you want by specifying the ViewName property. I follow the original HttpStatusCodeResult to set the HttpContext.Response.StatusCode and HttpContext.Response.StatusDescription but then base.ExecuteResult(context) will render the suitable view because again I derive from ViewResult. Simple enough is it? Hope this will be implemented in the MVC core.
See my BaseController bellow:
using System.Web;
using System.Web.Mvc;
namespace YourNamespace.Controllers
{
public class BaseController : Controller
{
public BaseController()
{
ViewBag.MetaDescription = Settings.metaDescription;
ViewBag.MetaKeywords = Settings.metaKeywords;
}
protected new HttpNotFoundResult HttpNotFound(string statusDescription = null)
{
return new HttpNotFoundResult(statusDescription);
}
protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null)
{
return new HttpUnauthorizedResult(statusDescription);
}
protected class HttpNotFoundResult : HttpStatusCodeResult
{
public HttpNotFoundResult() : this(null) { }
public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { }
}
protected class HttpUnauthorizedResult : HttpStatusCodeResult
{
public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { }
}
protected class HttpStatusCodeResult : ViewResult
{
public int StatusCode { get; private set; }
public string StatusDescription { get; private set; }
public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { }
public HttpStatusCodeResult(int statusCode, string statusDescription)
{
this.StatusCode = statusCode;
this.StatusDescription = statusDescription;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
context.HttpContext.Response.StatusCode = this.StatusCode;
if (this.StatusDescription != null)
{
context.HttpContext.Response.StatusDescription = this.StatusDescription;
}
// 1. Uncomment this to use the existing Error.ascx / Error.cshtml to view as an error or
// 2. Uncomment this and change to any custom view and set the name here or simply
// 3. (Recommended) Let it commented and the ViewName will be the current controller view action and on your view (or layout view even better) show the #ViewBag.Message to produce an inline message that tell the Not Found or Unauthorized
//this.ViewName = "Error";
this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
base.ExecuteResult(context);
}
}
}
}
To use in your action like this:
public ActionResult Index()
{
// Some processing
if (...)
return HttpNotFound();
// Other processing
}
And in _Layout.cshtml (like master page)
<div class="content">
#if (ViewBag.Message != null)
{
<div class="inlineMsg"><p>#ViewBag.Message</p></div>
}
#RenderBody()
</div>
Additionally you can use a custom view like Error.shtml or create new NotFound.cshtml like I commented in the code and you may define a view model for the status description and other explanations.
protected override void HandleUnknownAction(string actionName)
{
ViewBag.actionName = actionName;
View("Unknown").ExecuteResult(this.ControllerContext);
}
Here is true answer which allows fully customize of error page in single place.
No need to modify web.confiog or create sophisticated classes and code.
Works also in MVC 5.
Add this code to controller:
if (bad) {
Response.Clear();
Response.TrySkipIisCustomErrors = true;
Response.Write(product + I(" Toodet pole"));
Response.StatusCode = (int)HttpStatusCode.NotFound;
//Response.ContentType = "text/html; charset=utf-8";
Response.End();
return null;
}
Based on http://www.eidias.com/blog/2014/7/2/mvc-custom-error-pages
Please follow this if you want httpnotfound Error in your controller
public ActionResult Contact()
{
return HttpNotFound();
}

How to generate ActionLink inside Threads

I've got a thread that sends emails around. I need to generate ActionLinks as part of the content of the email so the user can click on the link and be redirected to the website, exactly to the required page. I tried to instantiate a UrlHelper class and use it's Action method to generate the link but since threads don't run within the context of any request I get exceptions at the time of generating the ActionLink.
How can I do this?
You need to fake HttpContextBase and pass this to an UrlHelper which you can use in a thread without an HttpContext. Here is the rough idea, although you will need to create a class around it etc, this is a quick proof of concept as unit tests don't have an HttpContext either.
[TestFixture]
public class RouteTestClass
{
private UrlHelper helper;
public RouteTestClass()
{
MvcApplication.RegisterRoutes(RouteTable.Routes); //You dont need to do this if its done in global.asax!
var c = new RequestContext(new FakeContext(), new RouteData());
helper = new UrlHelper(c, RouteTable.Routes);
}
[Test]
public void TestGetHomeIndex()
{
var url = helper.Action("Index", "Home");
Assert.AreEqual("/",url);
}
}
public class FakeContext : HttpContextBase
{
public override HttpRequestBase Request { get { return new FakeRequest(); } }
public override HttpResponseBase Response { get { return new FakeResponse(); } }
}
public class FakeRequest : HttpRequestBase
{
public override string ApplicationPath { get { return "/"; } }
public override NameValueCollection ServerVariables { get { return new NameValueCollection(); } }
}
public class FakeResponse : HttpResponseBase
{
public override string ApplyAppPathModifier(string virtualPath)
{
return virtualPath;
}
}
Edit
Looking at this answer, I tidied the code up a little as I don't need to create fakes for HttpRequestBase and HttpResponseBase myself.
[TestFixture]
public class RouteTestClass
{
private UrlHelper helper;
public RouteTestClass()
{
MvcApplication.RegisterRoutes(RouteTable.Routes);
var req = new HttpRequest("/", "http://www.yoururl.com", "");
var resp = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(req, resp);
var c = new RequestContext(new HttpContextWrapper(httpContext), new RouteData());
helper = new UrlHelper(c, RouteTable.Routes);
}
[Test]
public void TestGetHomeIndex()
{
var url = helper.Action("Index", "Home");
Assert.AreEqual("/",url);
}
}
You can give the thread access to an existing UrlHelper by passing it to the thread starter. If your thread is started from a controller, just pass the UrlHelper in the controller's Url property:
new Thread(
urlHelper =>
{
var url =
((UrlHelper)urlHelper)
.Action("Index", "Home", new { Id = 5 });
// use url here
}
).Start(Url);

Resources