Recently I've been following some WebApi2 tutorials. I have a situation whereby if a requested GET operation returns data outside of the user's remit, then I need to return a Forbidden code.
Imports System.Net
Imports System.Net.Http
Imports System.Web.Http
Namespace Controllers
Public Class MyController
Inherits ApiController
<Route("Records/{id}")>
Public Function [Get](id As Int32) As IHttpActionResult
If Not Remit.IsWithinRemit(id) Then
Return Request.CreateErrorResponse(HttpStatusCode.Forbidden, "This data is not within your remit")
Else
Dim r As New CustomObject(id)
Return Ok(r)
End If
End Function
End Class
End Namespace
Unfortunately, although the Ok(r) part works okay, CreateErrorResponse throws an InvalidCastException:
Unable to cast object of type 'System.Net.Http.HttpResponseMessage' to type 'System.Web.Http.IHttpActionResult'.
I know why the error is happening, but am unsure of the correct approach of how to fix it.
In other threads, people advise that CreateErrorResponse() is the best approach for WebApi2, but VS creates it's sample GET request returning IHttpActionResult. Its like stuff doesn't seem to fit together for us newbies at the moment...
No, it isn't obvious, but you can get what you want (error code plus message) AND return it from a method of type IHttpActionResult. No need to change the return type or go without error messages.
This is the helper class:
public class ErrorResult : IHttpActionResult
{
private HttpRequestMessage Request { get; }
private HttpStatusCode statusCode;
private string message;
public ErrorResult(HttpRequestMessage request, HttpStatusCode statusCode, string message)
{
this.Request = request;
this.statusCode = statusCode;
this.message = message;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Request.CreateErrorResponse(statusCode, message));
}
}
and you can call it like this:
public IHttpActionResult MyMethod()
{
MyServiceLayer myServiceLayer = new MyServiceLayer();
MyType myvar;
if (MyServiceLayer.EverythingIsOK(ref myvar))
return Ok(myvar);
else
return new ErrorResult(Request, HttpStatusCode.SomeErrorCode, "Something Is Wrong");
}
try this
Change your Get method to return "HttpResponseMessage"
<Route("Records/{id}")>
Public Function [Get](id As Int32) As HttpResponseMessage
If Not Remit.IsWithinRemit(id) Then
Return Request.CreateResponse(HttpStatusCode.Forbidden, "This data is not within your remit")
Else
Dim r As New CustomObject(id)
Return Request.CreateResponse(HttpStatusCode.OK, r)
End If
End Function
Check below link
http://www.asp.net/web-api/overview/web-api-routing-and-actions/action-results
I found an alternative possible solution (there may be better but this one works and is simple). It returns
403 Forbidden
but with no content:
<Route("Records/{id}")>
Public Function [Get](id As Int32) As IHttpActionResult
If Not Remit.IsWithinRemit(id) Then
Return New Results.StatusCodeResult(HttpStatusCode.Forbidden, Request)
Else
Dim r As New CustomObject(id)
Return Ok(r)
End If
End Function
Because HttpResponseMessage comes from the same namespace, and also allows you to return custom error messages in addition to a HTTP status code, that option is more suitable to use in most cases.
I guess IHttpActionResult is for basic status code returns with no frills. I posted this alongside the above answer to give new coders visibility of both options.
I encountered the same error using .NET Framework 4.7.2. Rather than changing the return type** from IHttpActionResult, I corrected the bug by wrapping myHttpResponseMessage object in a ResponseMessageResult like this: -
return new ResponseMessageResult(myHttpResponseMessage);
** it was a single, custom case amongst a set of straightforward IHttpActionResult cases in a switch block
Related
I've RESTful service Spring MVC based.
The service has a RESTful resource method that returns the following response:
public class OperationalDataResponse<T> {
private String status;
private String statusMessage;
private T result;
//getters and setters
}
This response object encapsulates the result object of type T.
On the client side I use RestTemplate with GsonHttpMessageConverter added.
I get the response from service as a ResponseEntity
I handle the generic response with runtime Type as below:
public class OperationalDataRestClient<REQ,RESULT_TYPE> {
public OperationalDataResponse<RESULT_TYPE> getOperationalData(String resourcePath, Map<String, Object> urlVariables, Class<RESULT_TYPE> resultType) {
//code to invoke service and get data goes here
String responseString = responseEntity.getBody();
response = GsonHelper.getInstance().fromJson(responseString, getType(OperationalDataResponse.class, resultType));
}
Type getType(final Class<?> rawClass, final Class<?> parameter) {
return new ParameterizedType() {
#Override
public Type[] getActualTypeArguments() {
return new Type[] { parameter };
}
#Override
public Type getRawType() {
return rawClass;
}
#Override
public Type getOwnerType() {
return null;
}
};
}
}
This works like a charm as long as my resultType is a non-collection class.
So, this works great from caller code:
getOperationalData(someResourcePath, someUrlVariables, MyNonGenericClass.class)
However if my resultType is a collection (say, List<String> or List<MyNonGenericClass>)
then I don't know how to pass the resultType Class from the caller code.
For example, from caller code,
getOperationalData(someResourcePath, someUrlVariables, List.class)
or
getOperationalData(someResourcePath, someUrlVariables, List<MyNonGenericClass>.class)
throws compilation error.
I tried passing on ArrayList.class as well but that too doesn't work.
Any suggestion how can I pass a generic collection as a resultType from caller code (in other words, as an example, how can I pass the class object of a List<String> or List<MyNonGenericClass> from caller code ?)
If you know that ResultType is coming as a List, Then it will obvious fail like you said compilation issue.Why? because you are trying to send a List when you method only accepts a single value.In order to over come that issue you will have to change the method arguments to the following
public OperationalDataResponse<RESULT_TYPE> getOperationalData(String resourcePath, Map<String, Object> urlVariables, List<Class<RESULT_TYPE>> resultType){
....
}
and you will have to make some slight modification to getType() Method,loop it and then pass each class value to getType method like so
for(MyNonGenericClass myClass:mylist){
getType(OperationalDataResponse.class, myClass.getClass());
}
I have a problem with an XML response to a call to the Web API.
Specifically, I have a function "GetValue" call that when I should return in XML format based to the id or class "Cellulare" or class "Televisore".
The problem is that if I make a request from browser gives me the following error:
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
This is the example:
Public Class Cellulare
Public Property Colore As String
Public Property SistemaOperativo As String
End Class
Public Class Televisore
Public Property Colore As String
Public Property Marca As String
End Class
Public Function GetValue(ByVal id As Integer) // ' As Cellulare
If Id = 1 Then
Dim MyTelevisore As New Televisore
MyTelevisore.Colore = "grigio"
MyTelevisore.Marca = "lg"
Return MyTelevisore
Else
Dim MyCellulare As New Cellulare
MyCellulare.Colore = "nero"
MyCellulare.SistemaOperativo = "android"
Return MyCellulare
End If
End Function
Can anyone help me to solve this problem???
Thank in advance
greetings
Donato
I think your approach is wrong.
You have simple objects to return, that can be handled easily by the default serializers webapi has to offer.
Your returned object type should be IHttpActionResult (webapi2) or HttpResponseMessage.
I would NOT go for what #Frank Witte suggested, cause returning the object itself is bad practice. Specifically here you can just return a generic object through IHttpActionResult / HttpResponseMessage.
You should do something like:
Public Function GetValue(ByVal id As Integer) As IHttpActionResult
If Id = 1 Then
Dim MyTelevisore As New Televisore
MyTelevisore.Colore = "grigio"
MyTelevisore.Marca = "lg"
Return Ok(MyTelevisore)
Else
Dim MyCellulare As New Cellulare
MyCellulare.Colore = "nero"
MyCellulare.SistemaOperativo = "android"
Return Ok(MyCellulare)
End If
End Function
It throws the error because you do not supply any return type to your GetValue function. You commented that out.
As I can tell from your code you are returning a different type of object depending on the id you supply to the GetValue call. I do not know the complete context of what you are trying to do, but from what I can see it would make more sense to have a different controller, or route at least, for the different types of object:
/api/cellulare/<id>
Would map to a controller CellulareController.
/api/televisore/<id>
Would map to a controller TelevisoreController. Each with their own Get(), Post() and Delete() methods if you will.
Hope this helps.
I have this Object
public class Deportista implements Serializable {
private static final long serialVersionUID = 6229604242306465153L;
private String id;
...
#NotNull(message="{field.null}")
public String getId() {
return id;
}
...
}
I have the following Controller's methods
#InitBinder(value="deportistaRegistrar")
public void registrarInitBinder(WebDataBinder binder) {
logger.info(">>>>>>>> registrarInitBinder >>>>>>>>>>>>>");
}
#RequestMapping(value="/registrar.htm", method=RequestMethod.GET)
public String crearRegistrarFormulario(Model model){
logger.info("crearRegistrarFormulario GET");
Deportista deportista = new Deportista();
model.addAttribute("deportistaRegistrar", deportista);
return "deportista.formulario.registro";
}
#RequestMapping(value="/registrar.htm", method=RequestMethod.POST)
public String registrarPerson(#Validated #ModelAttribute("deportistaRegistrar") Deportista deportista,
BindingResult result){
logger.info("registrarPerson POST");
logger.info("{}", deportista.toString());
if(result.hasErrors()){
logger.error("There are errors!!!!");
for(ObjectError objectError : result.getAllErrors()){
logger.error("Error {}", objectError);
}
return "deportista.formulario.registro";
}
logger.info("All fine!!!!");
this.fakeMultipleRepository.insertDeportista(deportista);
return "redirect:/manolo.htm";
}
Until here the Controller is able to create a form (GET) and submit (POST) a new command object, Validation code works well.
The problem is with the update.
I have the following:
#InitBinder(value="deportistaActualizar")
public void actualizarInitBinder(WebDataBinder binder) {
logger.info(">>>>>>>> actualizarInitBinder >>>>>>>>>>>>>");
binder.setDisallowedFields("id");
}
Observe I have binder.setDisallowedFields("id")
public String crearActualizarFormulario(#PathVariable("id") String id, Model model){
logger.info("crearActualizarFormulario GET");
Deportista deportista = this.fakeMultipleRepository.findDeportista(id);
model.addAttribute("deportistaActualizar", deportista);
return "deportista.formulario.actualizacion";
}
#RequestMapping(value="/{id}/actualizar.htm", method=RequestMethod.POST)
public String actualizarPerson(#Validated #ModelAttribute("deportistaActualizar") Deportista deportista,
BindingResult result){
logger.info("actualizarPerson POST");
logger.info("{}", deportista.toString());
if(result.hasErrors()){
logger.error("There are errors!!!!");
for(ObjectError objectError : result.getAllErrors()){
logger.error("Error {}", objectError);
}
return "deportista.formulario.actualizacion";
}
logger.info("All fine!!!!");
this.fakeMultipleRepository.updateDeportista(deportista);
return "redirect:/manolo.htm";
}
The problem is:
when the form or command has any error, the controller re-render the view and the form appear showing the error messages how is expected, but without the ID value
or
if I try to update the object, of course keeping the id value, and without any error to simply proceed to update, it fails
The following appears in the Console:
- -------- createCollections ---------------
- >>>>>>>> actualizarInitBinder >>>>>>>>>>>>>
- Skipping URI variable 'id' since the request contains a bind value with the same name.
- actualizarPerson POST
- Deportista [id=null, nombre=Manuel, ...]
- There are errors!!!!
- Error Field error in object 'deportistaActualizar' on field 'id': rejected value [null]; codes [NotNull.deportistaActualizar.id,NotNull.id,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [deportistaActualizar.id,id]; arguments []; default message [id]]; default message [The field must be not empty]
The id is null. How I can around this problem keeping the Request Scope?
I have an alternate controller which is working with #SessionAttributes and all works perfect. But since is a huge risk if the user has many tabs open in the same web browser, one for create and other for updating, all is going to be very wrong. According with Spring MVC + Session attributes and multiple tabs, request scope should be used instead of session scope. It has sense.
Sadly seems Spring is not going to fix this:
#SessionAttributes doesn't work with tabbed browsing
Addition
According with your suggestion, I have the following:
#ModelAttribute("deportistaActualizar")
public Deportista populateActualizarFormulario(#RequestParam(defaultValue="") String id){
logger.info("populateActualizarFormulario - id: {}", id);
if(id.equals(""))
return null;
else
return this.fakeMultipleRepository.findDeportista(id);
}
Observe the method uses #RequestParam, my problem is how update that method to work when the URL to update has the following style
http://localhost:8080/spring-utility/deportista/1/actualizar.htm. There is no param in the URL, therefore #RequestParam is useless now.
I already have read the Spring Reference documentation:
Using #ModelAttribute on a method
Second Addition
Yes, you was right, and I did that yesterday, but I forget to share the following:
#ModelAttribute("deportistaActualizar")
public Deportista populateActualizarFormulario(#PathVariable(value="id") String id){
logger.info("populateActualizarFormulario - id: {}", id);
if(id.equals(""))
return null;
else
return this.fakeMultipleRepository.findDeportista(id);
}
Since a #ModelAttribute is called always before by any handler method, the following URL fails http://localhost:8080/spring-utility/deportista/registrar.htm, the following appears on the page
HTTP Status 400 -
type Status report
message
description The request sent by the client was syntactically incorrect.
Of course because the URL does not contains the expected id. Therefore I can't create new records to later edit/see.
I can confirm, that for the following work:
http://localhost:8080/spring-utility/deportista/1/detalle.htm
http://localhost:8080/spring-utility/deportista/1/actualizar.htm
the id (1) is retrieved.
How I could resolve this?
Thank You
I have the following method skeleton in a Spring MVC application:
#RequestMapping(value = "/activateMember/{token}", method = RequestMethod.GET, produces = "text/html")
public String activateMember(#PathVariable("token") String token) {
...
}
I am trying to display an error message if the token is invalid for some reason. However I have no ModelAttribute in the method arguments and I don't really want one. But of course I can't use an Errors or BindingResults argument because of the absence of a ModelAttribute and its corresponding form.
So my question is:
what is the recommended way to display an error message given the above method signature and without introducing a ModelAttribute?
If the String you've returned from the method is a viewname (Spring default) then simply create a view for this case and do like:
#RequestMapping()
public String activateMember(#PathVariable("token") String token) {
if(checkToken(token)){
doProcess();
return "userprofile";
} else {
return "badtoken"
}
}
In more complicated case you may have a hierarchy of exceptions, related to bad tokens. (Token is expired, token is just incorrect and so on). You can register an #ExceptionHandler in the same controller:
#RequestMapping()
public String activateMember(#PathVariable("token") String token) {
return activate(token); // This method may throw TokenException and subclasses.
}
#ExceptionHandler(TokenException.class)
public ModelAndView tokenException(TokenException e){
// some code
return new ModelAndView("badtoken", "exception", e);
}
I am trying to mock out HttpContext so that I can unit test my controller's Request.IsAuthenicated call. I am using the code that I found at Scott Hanselman's blog to simulate HttpContext using rhino.mocks.
so i have this unit test piece:
PostsController postsController = new PostsController(postDL);
mocks.SetFakeControllerContext(postsController);
Expect.Call(postsController.Request.IsAuthenticated).Return(true);
In my controller action, I have something like
if(Request.IsAuthenticated)....
when I try to run the unit test, the test fails throwing a null exception, and when I try to debug the unit test, I see that the HttpContext is never assigned to the controller.
any ideas?
This should work:
PostsController postsController = new PostsController(postDL);
var context = mocks.Stub<HttpContextBase>();
var request = mocks.Stub<HttpRequestBase>();
SetupResult.For(request.IsAuthenticated).Return(true);
SetupResult.For(context.Request).Return(request);
postsController.ControllerContext = new ControllerContext(context, new RouteData(), postsController);
This may be of some use to you, worked for me in a similar scenario:
http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx
You may find the post I wrote on this to be helpful in some way
http://santoshbenjamin.wordpress.com/2008/08/04/mock-httpcontext-and-session-state/
cheers
benjy
Now, for disclosure, I have yet to get my hands dirty with most of the stuff you are working with, however:
If you want to mock the IsAuthenticated, why not just create a static class to return a bool that can the be manipulated by your test code?
This is a bit rough round the edges, but hopefully you get the idea:
interface IAuthenticationChecker
{
bool IsAuthenticated { get; }
}
public class MockAuthenticationChecker : IAuthenticationChecker
{
static bool _authenticated = false;
public static void SetAuthenticated(bool value)
{
_authenticated = value;
}
#region IAuthenticationChecker Members
public bool IsAuthenticated
{
get { return _authenticated; }
}
#endregion
}
public class RequestAuthenticationChecker : IAuthenticationChecker
{
#region IAuthenticationChecker Members
public bool IsAuthenticated
{
get {
if (HttpContext.Current == null)
throw new ApplicationException(
"Unable to Retrieve IsAuthenticated for Request becuse there is no current HttpContext.");
return HttpContext.Current.Request.IsAuthenticated;
}
}
#endregion
}
You can then use a reference to either at app level, yeah it means you have to add a reference at app level, and you need to use a different ref rather than Request, but you also get complete control over the authentication for testing :)
FYI - this is totally open to being blown apart, I threw it together in about a minute :)
Here is one simple way to fake the context, found it from Jeff's blog :
TextWriter tw = new StringWriter();
HttpWorkerRequest wr = new SimpleWorkerRequest("/webapp", "c:\\inetpub\\wwwroot\\webapp\\", "default.aspx", "", tw);
HttpContext.Current = new HttpContext(wr);
Here's a class that may be useful. It handles ajax requests, user authentication, request parameters and more: https://gist.github.com/3004119