I implemented compression per the solution here:
Compress HTTP GET Response
However, my Delete Web API is throwing an exception:
public HttpResponseMessage Delete(int id)
{
if (_repo == null)
{
_uow = DependencyResolver.Current.GetService<TPS.Data.Can.IUnitOfWork>();
_repo = _uow.TradeSpendRepository;
}
if (!_repo.Delete(id))
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
_uow.Save();
return Request.CreateResponse(HttpStatusCode.OK);
}
The exception is thrown in the CompressedContent's constructor because content is null:
if (content == null)
{
throw new ArgumentNullException("content");
}
I guess returning a status code isn't enough! What's the best approach to prevent this exception?
Since there is no content here, there is no need of creating a CompressedContent here, so you should add an additional check in the message handler.
Example:
if (response.Content != null && response.RequestMessage.Headers.AcceptEncoding != null)
{
string encodingType = response.RequestMessage.Headers.AcceptEncoding.First().Value;
response.Content = new CompressedContent(response.Content, encodingType);
}
Related
I have a rest endpoint to deliver a file from the file system to the user through a FileSystemResource. If the file wasn't found it should display my custom 404 error page. But how can I achieve this? If I simply return a ResponseEntity with the status 404 the default error page is shown and not mine. I have other MVC controllers where if I enter an invalid URL my custom error page is returned. I tried to return a ModelAndView object and other things, but none seem to work.
Here an example for the download endpoint (just the important parts):
#GetMapping("/download/{fileName}")
public ResponseEntity<FileSystemResource> downloadFile(#PathVariable("fileName") String fileName) {
FileSystemResource fileResource= new FileSystemResource(STORAGE_LOCATION + fileName);
if (fileResource.exists()) {
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(ContentDisposition.parse("attachment; filename=" + fileName));
headers.setContentLength(fileResource.contentLength());
return new ResponseEntity<>(fileResource, headers, HttpStatus.OK);
} else {
//what to return here
}
}
The MVC part is covered by a custom error controller, taken from some examples I found:
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.FORBIDDEN.value()) {
return "error/403";
} else if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "error/404";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "error/500";
}
}
return "error/default";
}
#Override
public String getErrorPath() {
return "/error";
}
}
I didn't find this yesterday (searched and tried stuff the whole afternoon), but here is the quick and easy solution since spring 5, just:
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
I'm working on an API being developed with .net Web Api 2. I've seen many blog posts and SO questions about Web Api version 1, but answers using the changes made in version 2 seem to be scarce by comparison.
Compare these two ways of handling 'errors' in a controller ItemsController
A. Using methods that create objects from System.Web.Http.Results
// GET api/user/userID/item/itemID
[Route("{itemID:int}", Name="GetItem")]
[ResponseType(typeof(ItemDTO))]
public IHttpActionResult Get(int userID, int itemID)
{
if (userID < 0 || itemID < 0) return BadRequest("Provided user id or item id is not valid");
ItemDTO item = _repository.GetItem(itemID);
if (item == null) return NotFound();
if (item.UserID != userID) return BadRequest("Item userID does not match route userID");
return Ok<ItemDTO>(item);
}
B. Throwing exceptions that can be caught by registering a custom Global Exception Handler
// ex) in WebApiConfig.cs
// config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
public class GlobalExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
Exception exception = context.Exception;
HttpException httpException = exception as HttpException;
if (httpException != null)
{
context.Result = new SimpleErrorResult(context.Request, (HttpStatusCode)httpException.GetHttpCode(), httpException.Message);
return;
}
if (exception is RootObjectNotFoundException)
{
context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.NotFound, exception.Message);
return;
}
if (exception is BadRouteParametersException || exception is RouteObjectPropertyMismatchException)
{
context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.BadRequest, exception.Message);
return;
}
if (exception is BusinessRuleViolationException)
{
context.Result = new SimpleErrorResult(context.Request, (HttpStatusCode)422, exception.Message);
return;
}
context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.InternalServerError, exception.Message);
}
}
GET api/user/userID/item/itemID
[Route("{itemID:int}", Name="GetItem")]
[ResponseType(typeof(ItemDTO))]
public IHttpActionResult Get(int userID, int itemID)
{
if (userID < 0 || itemID < 0)
throw new BadRouteParametersException("Provided user or item ID is not valid");
ItemDTO item = _repository.GetItem(itemID);
if (item.UserID != userID)
throw new RouteObjectPropertyMismatchException("Item userID does not match route userID");
return Ok<ItemDTO>(item);
}
Both of these seem like valid options. Since I am able to return System.Web.Http.Results objects it seems like solution A. is the best one.
But consider when in my _repository my GetItem method is implemented like so
public ItemDTO GetItem(int itemId)
{
ItemInfo itemInfo = ItemInfoProvider.GetItemInfo(itemId);
if (itemInfo == null) throw new RootObjectNotFoundException("Item not found");
ItemDTO item = _autoMapper.Map<ItemDTO>(itemInfo);
return item;
}
Here I can skip calling the autoMapper on null in GetItem and also skip checking for null in the controller.
Questions
Which way makes more sense?
Should I attempt a combination of A & B?
Should I try to keep my Controllers thin or should this type of validation & processing logic be kept there since I have access to the NotFound() and BadRequest() methods?
Should I be performing this type of logic somewhere else in the framework pipeline?
I realize my question is more architectural rather than 'how do i use this feature' but again, I haven't found too many explanations of how and when to use these different features.
From my standpoint, a global exception handler makes unit testing each action easier (read: more legible). You're now checking against a specific [expected] exception versus (essentially) comparing status codes. (404 vs. 500 vs. etc.) It also makes changes/logging of error notifications (at a global/unified level) much easier as you have a single unit of responsibility.
For instance, which unit test do you prefer to write?
[Test]
public void Id_must_not_be_less_than_zero()
{
var fooController = new FooController();
var actual = fooController.Get(-1);
Assert.IsInstanceOfType(actual, typeof(BadRequestResult));
}
[Test]
[ExpectedException(typeof(BadRouteParametersException))]
public void Id_must_not_be_less_than_zero()
{
var fooController = new FooController();
var actual = fooController.Get(-1);
}
Generally speaking, I would say this is more a preference than a hard-and-fast rule, and you should go with whatever you find to be the most maintainable and easiest to understand from both an on-boarding perspective (new eyes working on the project) and/or later maintenance by yourself.
As Brad notes, this partly comes down to preference.
Using HTTP codes is consistent with how the web works, so it's the way I lean.
The other consideration is that throwing exceptions has a cost. If you're OK with paying that cost, and take that into account in your design, it's fine to make that choice. Just be aware of it, particularly when you're using exceptions for situations that aren't really exceptional but rather are things you know you may encounter as part of normal application flow.
It's an older post, but there's an interesting discussion on the topic of exceptions and performance here:
http://blogs.msdn.com/b/ricom/archive/2006/09/14/754661.aspx
and the follow-up:
http://blogs.msdn.com/b/ricom/archive/2006/09/25/the-true-cost-of-net-exceptions-solution.aspx
I have an asp.net MVC website which is consuming a rest api to receive it's data. I'm using asynchronous tasks to perform the requests as there can be many on each page. After a while of uptime the website has been throwing the following error when trying to receive data.
The underlying connection was closed: An unexpected error occurred on a send.
I read that this could be due to the maxconnection settings on the web.config but increasing this doesn't seem to make much difference.
I'm also using caching to reduce the load on the api. The task is cached so the result can be used later.
The only way I've found to fix this is by recycling the application pool. Any help would be appreciated.
/* Code from page_load */
var currenciesTask = ApiClient.GetAsync<CurrencyListWrapper>("currencies");
var blogArticleTask = ApiClient.GetAsync<BlogArticleListWrapper>("blog/articles", "limit=10");
var seoPageTask = ApiClient.GetAsync<SEOPageListWrapper>("seopages");
await Task.WhenAll(currenciesTask, blogArticleTask, seoPageTask);
/* Code from data access later */
public class ApiClient : HttpClient
{
public static Task<T> GetAsync<T>(string operation, string query = null, bool cache = true)
{
// Check if task is in cache
string cacheName = null;
if (cache)
{
cacheName = String.Format("{0}_{1}_{2}", operation, query ?? String.Empty, App.GetLanguage());
var cachedTask = HttpRuntime.Cache[cacheName];
if (cachedTask != null)
{
return (Task<T>)cachedTask;
}
}
// Get data task
var task = GetAsyncData<T>(operation, query);
// Add to cache if required
if (task != null && cache)
{
App.AddToCache(cacheName, task);
}
return task;
}
public static async Task<T> GetAsyncData<T>(string operation, string query = null)
{
using (ApiClient client = new ApiClient())
{
string url;
if (query != null)
{
url = String.Format("{0}?{1}", operation, query);
}
else
{
url = String.Format("{0}", operation);
}
var response = await client.GetAsync(url);
return (await response.Content.ReadAsAsync<T>());
}
}
}
This is wrong,
The task is cached so the result can be used later.
You are supposed to cache result, not the task. At end of first execution, your HttpClient is closed and when you try to retrieve cached task, it will not work.
public class ApiClient : HttpClient
{
public static async Task<T> GetAsync<T>(string operation, string query = null, bool cache = true)
{
// Check if task is in cache
string cacheName = null;
if (cache)
{
cacheName = String.Format("{0}_{1}_{2}", operation, query ?? String.Empty, App.GetLanguage());
T cachedResult = (T)HttpRuntime.Cache[cacheName];
if (cachedResult!= null)
{
return Task.FromResult(cachedResult);
}
}
// Get data task
var result = await GetAsyncData<T>(operation, query);
// Add to cache if required
if (result != null && cache)
{
App.AddToCache(cacheName, result);
}
return result;
}
public static async Task<T> GetAsyncData<T>(string operation, string query = null)
{
using (ApiClient client = new ApiClient())
{
string url;
if (query != null)
{
url = String.Format("{0}?{1}", operation, query);
}
else
{
url = String.Format("{0}", operation);
}
var response = await client.GetAsync(url);
return (await response.Content.ReadAsAsync<T>());
}
}
}
Akash could be right.
But it seems more or less connection issue with application pool. Set the connection limit 0 to make it unlimited at application pool.
Have a finally block in you code, and
gc.collect();
garbage collection method to be called to remove unused connections to make space for other connection.
I've implemented a custom exception filter to my Web API. It is working as intended, except for one small detail...
In the following code sample, SaveToErrorLog saves exception details and tries to get the request url from context.Request.RawUrl. But context.Request does not contain the url that the API tried to serve when the exception happened. Is there a way to get the url when using an exception filter like this?
public class APIExceptionFilter : ExceptionFilterAttribute
{
private HttpContextBase context;
public APIExceptionFilter()
{
context = new HttpContextWrapper(HttpContext.Current);
}
public override void OnException(HttpActionExecutedContext actionContext)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
if (actionContext != null && context != null)
{
facade.SaveToErrorLog(actionContext.Exception, context.Request);
}
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(actionContext.Exception.Message),
ReasonPhrase = "APIException"
});
}
}
As per the comment above by #emre_nevayeshirazi, you need to use the HttpActionExecutedContext. This gives you access to the request and then the required Uri.
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
var requestedUri = actionExecutedContext.Request.RequestUri;
//Do something
}
I was trying to write an interceptor using spring AOP.The interceptor will find if a request URL is a bookmark,if so will redirect to the authentication page.
Code Snippet:
public Object invoke(MethodInvocation invocation) throws Throwable {
logger.entering(this.getClass().getSimpleName(), "invoke", invocation);
Object result = null;
try {
// Logic to exclude the beans as per the list in the configuration.
boolean excluded = false;
for (String excludebean : excludedBeans) {
if (excludebean != null && excludebean.equalsIgnoreCase(invocation.getThis().getClass().getSimpleName())) {
excluded = true;
break;
}
}
// If the Target Method is "toString", then set EXCLUDE to TRUE and process the request
if(excluded == false && invocation.getMethod().getName().equalsIgnoreCase("toString"))
{
excluded = true;
}
// if user session object is available, then process the request or
// else forward to the configured view.
if (excluded || getSessionHolder().getUserVO() != null) {
result = invocation.proceed();
}
else {
logger.logp(Level.INFO, this.getClass().getSimpleName(),
"invoke(MethodInvocation)", "User Object is "+ getSessionHolder().getUserVO()
+ ". So redirecting user to home page");
result = new ModelAndView("redirect:/security/authenticate.do");
}
}
catch (Throwable ex) {
throw ex;
}
logger.exiting(this.getClass().getSimpleName(), "invoke");
return result;
}
When I debug the control comes inside the else block as expected but after I return the result,control goes to the handle method for the bookmarked URl ratehr than the handler for the redirect view.
Pls help me on this..Thanks in advance.
Why Do you need AOP for the interceptor. You can redirect easily using Regular interceptor.
public class RedirectInterceptor extends HandlerInterceptorAdapter{
private String redirectMapping;
public void setRedirectMapping(String redirectMapping) {
this.redirectMapping = **maintenanceMapping**;
}
//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
if (somethingHappened){
response.sendRedirect(redirectMapping);
return false;
} else
return true;
}
}