how to execute huge rest based url in wcf data services? - c#

I'm using Wcf data service(V3). From IOS App they will send Signature through URL. Problem is sometimes user enters long signature in that situation it is giving an error like "Url is too long". how can i fix this issue on wcf data services.
Advance Thanks.

If the message client want to give to service is large, it is recommended to use POST.
You can find the guide for Actions in WCF Data Service V3 here:
http://blogs.msdn.com/b/odatateam/archive/2011/10/17/actions-in-wcf-data-services.aspx
And here is quick demo for setting up a WCF DS service with Action support:
public class Service : DataService<Context>, IServiceProvider
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceActionAccessRule("*", ServiceActionRights.Invoke);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
}
public object GetService(Type serviceType)
{
return typeof(IDataServiceActionProvider) == serviceType ? new ActionProvider() : null;
}
}
public class ActionProvider : IDataServiceActionProvider, IDataServiceActionResolver
{
private static List<ServiceAction> actions;
static ActionProvider()
{
ServiceAction movieRateAction = new ServiceAction(
"Action1", // name of the action
ResourceType.GetPrimitiveResourceType(typeof(string)), // no return type i.e. void
null, // no return type means we don’t need to know the ResourceSet so use null.
OperationParameterBindingKind.Never,
new ServiceActionParameter[] {
new ServiceActionParameter("val", ResourceType.GetPrimitiveResourceType(typeof(string)))
}
);
movieRateAction.SetReadOnly();
actions = new List<ServiceAction>() { movieRateAction };
}
public IEnumerable<ServiceAction> GetServiceActions(DataServiceOperationContext operationContext)
{
return actions;
}
public bool TryResolveServiceAction(DataServiceOperationContext operationContext, string serviceActionName,
out ServiceAction serviceAction)
{
serviceAction = null;
return false;
}
public IEnumerable<ServiceAction> GetServiceActionsByBindingParameterType(DataServiceOperationContext operationContext,
ResourceType bindingParameterType)
{
return Enumerable.Empty<ServiceAction>();
}
public IDataServiceInvokable CreateInvokable(DataServiceOperationContext operationContext, ServiceAction serviceAction,
object[] parameterTokens)
{
return new DataServiceInvokable(parameterTokens);
}
public bool AdvertiseServiceAction(DataServiceOperationContext operationContext, ServiceAction serviceAction, object resourceInstance, bool resourceInstanceInFeed, ref ODataAction actionToSerialize)
{
actionToSerialize = null;
return false;
}
public bool TryResolveServiceAction(DataServiceOperationContext operationContext, ServiceActionResolverArgs resolverArgs, out ServiceAction serviceAction)
{
serviceAction = actions[0];
return true;
}
}
public class DataServiceInvokable : IDataServiceInvokable
{
private readonly object[] parameters;
private string result;
public DataServiceInvokable(object[] parameters)
{
this.parameters = parameters;
}
public object GetResult()
{
return result;
}
public void Invoke()
{
result = parameters[0] as string;
}
}
Then you could send a POST request to http://example.org/service.svc/Action1
Header:
Content-Type: Application/json
Request Body:
{"val":"MessageToPostHere..."}

If you are using .Net 4.0 or above, you could experiment with your web.config settings file, with this:
<system.web>
...
<httpRuntime maxUrlLength="500" />
....
</system.web>

Related

I can t use SessionService class in asp.net web api

I have a class derived from webapi controller.In that controller i have a method
[HttpGet]
[Route("MessageCount")]
public string GetMessageCountCount()
{
OrganisationMembership organisationMembership = organisationService.Find(organisationId);
SessionService.Get.SetUserConversationMessages(organisationMembership);
return SessionService.Get.UnreadMessagesCount.ToString();
}
Here is SessionService
public class SessionService : ISessionService
{
private static SessionService _sessionService;
public static SessionService Get
{
get
{
if (_sessionService == null)
{
_sessionService = new SessionService(new RakletDb());
}
return _sessionService;
}
}
public int UnreadMessagesCount
{
get { return GetSessionInt("UnreadMessagesCount"); }
private set { SetSessionValue("UnreadMessagesCount", value); }
}
private int GetSessionInt(string key)
{
var s = HttpContext.Current.Session[key];
if (s != null)
{
return Int32.Parse(s.ToString());
}
return 0;
}
private void SetSessionValue(string key, object o)
{
HttpContext.Current.Session[key] = o;
}
public void SetUserConversationMessages(OrganisationMembership organisationMembership)
{
this.UnreadMessagesCount = somelinq query;
}
}
My project succesfully compiled. But when i call GetMessageCount method from browser it gives me an error like this:
error
{"Message":"An error has occurred.","ExceptionMessage":"Object reference not set to an instance of an object.","ExceptionType":"System.NullReferenceException","StackTrace":" at Services.SessionService.SetSessionValue(String key, Object o)...
I use something like this in a controller :SessionService.UnreadMessageCount
But in webapi I can only use SessionService.Get.UnreadMessageCount
so what problem can be?
The problem is on HttpContext.Current.Session[key] = o;,Because REST web api by design is stateless. So Default you can't use Session.
I would not suggest you use session on web api.
If you want to use Session,you need to make a HttpHandler which's implement IHttpHandler and let it implement System.Web.SessionState.IRequiresSessionState.
You might be like
public class SessionHander :
System.Web.Http.WebHost.HttpControllerHandler,
System.Web.SessionState.IRequiresSessionState
{
public SessionHander(RouteData routeData) : base(routeData)
{ }
}
public class SeesionRouterHanlder :
System.Web.Http.WebHost.HttpControllerRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new SessionHander(requestContext.RouteData);
}
}
And this code on Global.asax
var route = RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
route.RouteHandler = new SeesionRouterHanlder();

ServiceStack: How to unit test a service that serves files

I have a web service that service an Excel file
public class ReportService : Service
{
public IReportRepository Repository {get; set;}
public object Get(GetInvoiceReport request)
{
var invoices = Repository.GetInvoices();
ExcelReport report = new ExcelReport();
byte[] bytes = report.Generate(invoices);
return new FileResult(bytes);
}
}
and I setup the object that is retured from the service as
public class FileResult : IHasOptions, IStreamWriter, IDisposable
{
private readonly Stream _responseStream;
public IDictionary<string, string> Options { get; private set; }
public BinaryFileResult(byte[] data)
{
_responseStream = new MemoryStream(data);
Options = new Dictionary<string, string>
{
{"Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{"Content-Disposition", "attachment; filename=\"InvoiceFile.xlsx\";"}
};
}
public void WriteTo(Stream responseStream)
{
if (_responseStream == null)
return;
using (_responseStream)
{
_responseStream.WriteTo(responseStream);
responseStream.Flush();
}
}
public void Dispose()
{
_responseStream.Close();
_responseStream.Dispose();
}
}
Now, the webservice works fine when tested through a browser; but it gives an error message when tested from a unit test. Below is the error message:
System.Runtime.Serialization.SerializationException : Type definitions
should start with a '{', expecting serialized type 'FileResult', got
string starting with:
PK\u0003\u0004\u0014\u0000\u0008\u0000\u0008\u0000�\u000b5K���%\u0001\u0000\u0000�\u0003\u0000\u0000\u0013\u0000\u0000\u0000[Content_Types].xml��
at
ServiceStack.Text.Common.DeserializeTypeRefJson.StringToType(TypeConfig
typeConfig, StringSegment strType, EmptyCtorDelegate ctorFn,
Dictionary2 typeAccessorMap) at
ServiceStack.Text.Common.DeserializeType1.<>c__DisplayClass2_0.b__1(StringSegment value) at ServiceStack.Text.Json.JsonReader1.Parse(StringSegment
value) at ServiceStack.Text.Json.JsonReader1.Parse(String value)
at ServiceStack.Text.JsonSerializer.DeserializeFromString[T](String
value) at
ServiceStack.Text.JsonSerializer.DeserializeFromStream[T](Stream
stream) at
ServiceStack.ServiceClientBase.GetResponse[TResponse](WebResponse
webResponse) at
ServiceStack.ServiceClientBase.Send[TResponse](String httpMethod,
String relativeOrAbsoluteUrl, Object request)
Below is the unit test I used to test the webservice:
[Test]
public void TestInvoiceReport()
{
var client = new JsonServiceClient("http://localhost/report/");
var authResponse = client.Send(new Authenticate
{
provider = CredentialsAuthProvider.Name,
UserName = "[User Name]",
Password = "[Password]"
});
var requestDTO = new GetInvoiceReport();
var ret = client.Get<FileResult>(requestDTO);
Assert.IsTrue(ret != null);
}
Edit:
I am including the definition for my request DTO class:
[Route("/invoices", "GET")]
public class GetInvoiceReport: IReturn<FileResult>
{
}
Any help is appreciated.
Note: if you're making a HTTP Request instead of calling the Service in code, it's an Integration Test instead of a Unit Test.
You haven't provided your GetInvoiceReport Request DTO definition, but if you're returning anything that's not a serialized DTO it should be specified it its IReturn<T> interface, e.g:
public class GetInvoiceReport : IReturn<byte[]> { ... }
Then you'll be able to download the raw bytes with:
byte[] response = client.Get(new GetInvoiceReport());
You can use the Service Clients Request Filters for inspecting the HTTP Response Headers.
I'd also recommend checking out ServiceStack's .NET Service Clients docs which contains extensive info for downloading raw Responses.

Custom authorization IActionResults in aspnet-5 mvc-6

In ASP.NET 4 MVC5, I had this class that allowed me to return custom responses for unauthenticated responses to JSON endpoints. Here it is.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (IsAjax(filterContext))
{
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
success = false,
error = "You must be signed in."
}
};
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
private bool IsAjax(AuthorizationContext filterContext)
{
return filterContext.ActionDescriptor.GetFilterAttributes(true).OfType<AjaxAttribute>().FirstOrDefault() !=
null;
}
}
However, in MVC6, the new AuthorizeAttribute is no overrides for creating custom IActionResult results. How do I do this in MVC6?
A good point has been made by #blowdart in his comment about whether returning 401/403 should be the expected behaviour. In any case, I have tried a different approach for doing what the OP was asking, modifying the behavior of the default MVC authorization filters so that we return a json when user is unauthorized.
First thing I did was creating a new IAsyncAuthorizationFilter that will format the unauthorized result as a json for ajax request. It will basically:
Wrap an existing filter
Execute the wrapped filter
In case the user is unauthorized by the wrapped filter, return a json for ajax requests
This would be the CustomJsonAuthorizationFilter class:
public class CustomJsonAuthorizationFilter : IAsyncAuthorizationFilter
{
private AuthorizeFilter wrappedFilter;
public CustomJsonAuthorizationFilter(AuthorizeFilter wrappedFilter)
{
this.wrappedFilter = wrappedFilter;
}
public async Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
{
await this.wrappedFilter.OnAuthorizationAsync(context);
if(context.Result != null && IsAjaxRequest(context))
{
context.Result = new JsonResult(new
{
success = false,
error = "You must be signed in."
});
}
return;
}
//This could be an extension method of the HttpContext/HttpRequest
private bool IsAjaxRequest(Microsoft.AspNet.Mvc.Filters.AuthorizationContext filterContext)
{
return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
}
Then I have created an IApplicationModelProvider in order to wrap all existing AuthorizeFilter with the new custom filter. The AuthroizeFilter is added by AuthorizationApplicationModelProvider, but the new provider will be run after the default one since the order of the default provider is -990.
public class CustomFilterApplicationModelProvider : IApplicationModelProvider
{
public int Order
{
get { return 0; }
}
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
//Do nothing
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
this.ReplaceFilters(context.Result.Filters);
foreach(var controller in context.Result.Controllers)
{
this.ReplaceFilters(controller.Filters);
foreach (var action in controller.Actions)
{
this.ReplaceFilters(action.Filters);
}
}
}
private void ReplaceFilters(IList<IFilterMetadata> filters)
{
var authorizationFilters = filters.OfType<AuthorizeFilter>().ToList();
foreach (var filter in authorizationFilters)
{
filters.Remove(filter);
filters.Add(new CustomJsonAuthorizationFilter(filter));
}
}
}
Finally, update ConfigureServices in startup with the new application model provider:
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, CustomFilterApplicationModelProvider>());
I finally figured it out after looking at the source.
public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
Func<CookieRedirectContext, Task> _old;
public CustomCookieAuthenticationEvents()
{
_old = OnRedirectToLogin;
OnRedirectToLogin = OnCustomRedirectToLogin;
}
public Task OnCustomRedirectToLogin(CookieRedirectContext context)
{
var actionContext = context.HttpContext.RequestServices.GetRequiredService<IActionContextAccessor>();
if (actionContext.ActionContext == null)
return _old(context);
if (actionContext.ActionContext.ActionDescriptor.FilterDescriptors.Any(x => x.Filter is AjaxAttribute))
{
// this is an ajax request, return custom JSON telling user that they must be authenticated.
var serializerSettings = context
.HttpContext
.RequestServices
.GetRequiredService<IOptions<MvcJsonOptions>>()
.Value
.SerializerSettings;
context.Response.ContentType = "application/json";
using (var writer = new HttpResponseStreamWriter(context.Response.Body, Encoding.UTF8))
{
using (var jsonWriter = new JsonTextWriter(writer))
{
jsonWriter.CloseOutput = false;
var jsonSerializer = JsonSerializer.Create(serializerSettings);
jsonSerializer.Serialize(jsonWriter, new
{
success = false,
error = "You must be signed in."
});
}
}
return Task.FromResult(0);
}
else
{
// this is a normal request to an endpoint that is secured.
// do what ASP.NET used to do.
return _old(context);
}
}
}
Then, use this event class as follows:
services.Configure<IdentityOptions>(options =>
{
options.Cookies.ApplicationCookie.Events = new CustomCookieAuthenticationEvents();
});
ASP.NET 5 sure made simple things harder to do. Granted though, I can now customize things at a more granular level without effecting other pieces. Also, the source code is amazingly easy to read/understand. I am pretty happy having the confidence that any issue I am having can easily be identified as a bug or resolved by looking at the source.
Cheers to the future!

Return JSON with error status code MVC

I was trying to return an error to the call to the controller as advised in
This link so that client can take appropriate action.
The controller is called by javascript via jquery AJAX. I am getting the Json object back only if I don't set the status to error.
Here is the sample code
if (response.errors.Length > 0)
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(response);
I get the Json if I don't set the statuscode.
If I set the status code I get the status code back but not the Json error object.
Update
I want to send an Error object as JSON so that it can be handled error callback of ajax.
The neatest solution I've found is to create your own JsonResult that extends the original implementation and allows you to specify a HttpStatusCode:
public class JsonHttpStatusResult : JsonResult
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(object data, HttpStatusCode httpStatus)
{
Data = data;
_httpStatus = httpStatus;
}
public override void ExecuteResult(ControllerContext context)
{
context.RequestContext.HttpContext.Response.StatusCode = (int)_httpStatus;
base.ExecuteResult(context);
}
}
You can then use this in your controller action like so:
if(thereWereErrors)
{
var errorModel = new { error = "There was an error" };
return new JsonHttpStatusResult(errorModel, HttpStatusCode.InternalServerError);
}
I found the solution here
I had to create a action filter to override the default behaviour of MVC
Here is my exception class
class ValidationException : ApplicationException
{
public JsonResult exceptionDetails;
public ValidationException(JsonResult exceptionDetails)
{
this.exceptionDetails = exceptionDetails;
}
public ValidationException(string message) : base(message) { }
public ValidationException(string message, Exception inner) : base(message, inner) { }
protected ValidationException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
Note that I have constructor which initializes my JSON. Here is the action filter
public class HandleUIExceptionAttribute : FilterAttribute, IExceptionFilter
{
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception != null)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = ((ValidationException)filterContext.Exception).myJsonError;
}
}
Now that I have the action filter, I will decorate my controller with the filter attribute
[HandleUIException]
public JsonResult UpdateName(string objectToUpdate)
{
var response = myClient.ValidateObject(objectToUpdate);
if (response.errors.Length > 0)
throw new ValidationException(Json(response));
}
When the error is thrown the action filter which implements IExceptionFilter get called and I get back the Json on the client on error callback.
There is a very elegant solution to this problem, just configure your site via web.config:
<system.webServer>
<httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough"/>
</system.webServer>
Source: https://serverfault.com/questions/123729/iis-is-overriding-my-response-content-if-i-manually-set-the-response-statuscode
A simple way to send a error to Json is control Http Status Code of response object and set a custom error message.
Controller
public JsonResult Create(MyObject myObject)
{
//AllFine
return Json(new { IsCreated = True, Content = ViewGenerator(myObject));
//Use input may be wrong but nothing crashed
return Json(new { IsCreated = False, Content = ViewGenerator(myObject));
//Error
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return Json(new { IsCreated = false, ErrorMessage = 'My error message');
}
JS
$.ajax({
type: "POST",
dataType: "json",
url: "MyController/Create",
data: JSON.stringify(myObject),
success: function (result) {
if(result.IsCreated)
{
//... ALL FINE
}
else
{
//... Use input may be wrong but nothing crashed
}
},
error: function (error) {
alert("Error:" + erro.responseJSON.ErrorMessage ); //Error
}
});
Building on the answer from Richard Garside, here's the ASP.Net Core version
public class JsonErrorResult : JsonResult
{
private readonly HttpStatusCode _statusCode;
public JsonErrorResult(object json) : this(json, HttpStatusCode.InternalServerError)
{
}
public JsonErrorResult(object json, HttpStatusCode statusCode) : base(json)
{
_statusCode = statusCode;
}
public override void ExecuteResult(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_statusCode;
base.ExecuteResult(context);
}
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_statusCode;
return base.ExecuteResultAsync(context);
}
}
Then in your controller, return as follows:
// Set a json object to return. The status code defaults to 500
return new JsonErrorResult(new { message = "Sorry, an internal error occurred."});
// Or you can override the status code
return new JsonErrorResult(new { foo = "bar"}, HttpStatusCode.NotFound);
The thing that worked for me (and that I took from another stackoverflow response), is to set the flag:
Response.TrySkipIisCustomErrors = true;
You have to return JSON error object yourself after setting the StatusCode, like so ...
if (BadRequest)
{
Dictionary<string, object> error = new Dictionary<string, object>();
error.Add("ErrorCode", -1);
error.Add("ErrorMessage", "Something really bad happened");
return Json(error);
}
Another way is to have a JsonErrorModel and populate it
public class JsonErrorModel
{
public int ErrorCode { get; set;}
public string ErrorMessage { get; set; }
}
public ActionResult SomeMethod()
{
if (BadRequest)
{
var error = new JsonErrorModel
{
ErrorCode = -1,
ErrorMessage = "Something really bad happened"
};
return Json(error);
}
//Return valid response
}
Take a look at the answer here as well
You need to decide if you want "HTTP level error" (that what error codes are for) or "application level error" (that what your custom JSON response is for).
Most high level objects using HTTP will never look into response stream if error code set to something that is not 2xx (success range). In your case you are explicitly setting error code to failure (I think 403 or 500) and force XMLHttp object to ignore body of the response.
To fix - either handle error conditions on client side or not set error code and return JSON with error information (see Sbossb reply for details).
Several of the responses rely on an exception being thrown and having it handled in the OnException override. In my case, I wanted to return statuses such as bad request if the user, say, had passed in a bad ID. What works for me is to use the ControllerContext:
var jsonResult = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = "whoops" };
ControllerContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return jsonResult;
And if your needs aren't as complex as Sarath's you can get away with something even simpler:
[MyError]
public JsonResult Error(string objectToUpdate)
{
throw new Exception("ERROR!");
}
public class MyErrorAttribute : FilterAttribute, IExceptionFilter
{
public virtual void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.Exception != null)
{
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult() { Data = filterContext.Exception.Message };
}
}
}
If you are just using MVC the simplest way is to use HttpStatusCodeResult.
public ActionResult MyAjaxRequest(string args)
{
string error_message = string.Empty;
try
{
// successful
return Json(args);
}
catch (Exception e)
{
error_message = e.Message;
}
return new HttpStatusCodeResult(500, error_message);
}
When the error is returned to the client you can display it or action it how you like.
request.fail(function (jqXHR) {
if (jqXHR.status == 500) {
alert(jqXHR.statusText);
}
})
I was running Asp.Net Web Api 5.2.7 and it looks like the JsonResult class has changed to use generics and an asynchronous execute method. I ended up altering Richard Garside's solution:
public class JsonHttpStatusResult<T> : JsonResult<T>
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(T content, JsonSerializerSettings serializer, Encoding encoding, ApiController controller, HttpStatusCode httpStatus)
: base(content, serializer, encoding, controller)
{
_httpStatus = httpStatus;
}
public override Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var returnTask = base.ExecuteAsync(cancellationToken);
returnTask.Result.StatusCode = HttpStatusCode.BadRequest;
return returnTask;
}
}
Following Richard's example, you could then use this class like this:
if(thereWereErrors)
{
var errorModel = new CustomErrorModel("There was an error");
return new JsonHttpStatusResult<CustomErrorModel>(errorModel, new JsonSerializerSettings(), new UTF8Encoding(), this, HttpStatusCode.InternalServerError);
}
Unfortunately, you can't use an anonymous type for the content, as you need to pass a concrete type (ex: CustomErrorType) to the JsonHttpStatusResult initializer. If you want to use anonymous types, or you just want to be really slick, you can build on this solution by subclassing ApiController to add an HttpStatusCode param to the Json methods :)
public abstract class MyApiController : ApiController
{
protected internal virtual JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings, Encoding encoding)
{
return new JsonHttpStatusResult<T>(content, httpStatus, serializerSettings, encoding, this);
}
protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings)
{
return Json(content, httpStatus, serializerSettings, new UTF8Encoding());
}
protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus)
{
return Json(content, httpStatus, new JsonSerializerSettings());
}
}
Then you can use it with an anonymous type like this:
if(thereWereErrors)
{
var errorModel = new { error = "There was an error" };
return Json(errorModel, HttpStatusCode.InternalServerError);
}
Here is the JsonResult override answer for ASP.NET v5+ . I have tested and it works just as well as in earlier versions.
public class JsonHttpStatusResult : JsonResult
{
private readonly HttpStatusCode _httpStatus;
public JsonHttpStatusResult(object data, HttpStatusCode httpStatus) : base(data)
{
_httpStatus = httpStatus;
}
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)_httpStatus;
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var services = context.HttpContext.RequestServices;
var executor = services.GetRequiredService<IActionResultExecutor<JsonResult>>();
return executor.ExecuteAsync(context, this);
}
}

HttpOperationHandlerFactory and InputParameters issue

I've written a custom OperationHandler for my WCF WebAPI project as follows:
public class AuthenticationOperationHandler : HttpOperationHandlerFactory
{
protected override Collection<HttpOperationHandler> OnCreateRequestHandlers(ServiceEndpoint endpoint, HttpOperationDescription operation)
{
var baseHandlers = base.OnCreateRequestHandlers(endpoint, operation);
if (operation.InputParameters.Where(p => p.Name.ToLower() == "username").Any() &&
operation.InputParameters.Where(p => p.Name.ToLower() == "password").Any())
{
baseHandlers.Add(new AuthenticateRequestHandler(string.Format("{0}:{1}", operation.InputParameters.Where(p => p.Name == "username").First ().Name, operation.InputParameters.Where(p => p.Name == "password").First().Name)));
}
else
{
throw new WebFaultException(HttpStatusCode.Forbidden);
}
return baseHandlers;
}
}
As well as this custom RequestHandler which is added to the pipeline:
public class AuthenticateRequestHandler : HttpOperationHandler<HttpRequestMessage, string>
{
public AuthenticateRequestHandler(string outputParameterName)
: base(outputParameterName)
{
}
public override string OnHandle(HttpRequestMessage input)
{
var stringValue = input.Content.ReadAsString();
var username = stringValue.Split(':')[0];
var password = stringValue.Split(':')[1];
var isAuthenticated = ((BocaMembershipProvider)Membership.Provider).ValidateUser(username, password);
if (!isAuthenticated)
{
throw new WebFaultException(HttpStatusCode.Forbidden);
}
return stringValue;
}
}
and this is my API Implementation:
[ServiceContract]
public class CompanyService
{
[WebInvoke(UriTemplate = "", Method = "POST")]
public bool Post(string username, string password)
{
return true;
}
}
My configuration in Global.asax file is
public static void RegisterRoutes(RouteCollection routes)
{
var config = HttpHostConfiguration.Create().SetOperationHandlerFactory(new AuthenticationOperationHandler());
routes.MapServiceRoute<AuthenticationService>("login", config);
routes.MapServiceRoute<CompanyService>("companies", config);
}
When trying to send a POST request to /companies I receive the following error message:
The HttpOperationHandlerFactory is unable to determine the input
parameter that should be associated with the request message content
for service operation 'Post'. If the operation does not expect content
in the request message use the HTTP GET method with the operation.
Otherwise, ensure that one input parameter either has it's
IsContentParameter property set to 'True' or is a type that is
assignable to one of the following: HttpContent, ObjectContent1,
HttpRequestMessage or HttpRequestMessage1.
on this line:
var baseHandlers = base.OnCreateRequestHandlers(endpoint, operation);
Any idea why this happens and how to fix this in order to force user send username/password parameters in each and every request and validate it against the Membership API afterwards?
To answer your question, your UriTemplate property is empty, which is why an exception is thrown. It should be set as follows:
UriTemplate = "&username={username}&password={password}"
There's still a bug in your code because both input parameters receive the same string, namely username=JohnDoe:password=qwerty
To solve your problem, this is a good article on how to implement HTTP basic authentication with WCF Web API.

Categories

Resources