Spring supports annotation based validation at the controller level.
(1) Is it necessary to do such validations also at the RestTemplate level for responses from REST calls?
If the answer is Yes:
(2) Will there be support for that at the RestTemplate to validate responses from rest calls sometime in the future?
If the answer is No:
(3) why?
It is 2020 now and I still do not see the requested feature in place.
The #Valid is nice to automatically validate e.g. a posted RequestBody.
But for the validation of the body of a ResponseEntity fetched via RestTemplate, I do not see any fancy equivalent.
So the only option I know, is to do it on your own taken from here. Input is the class of your RequestEntitys body. input is the body itself.
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Input>> violations = validator.validate(input);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
So to answer your question:
Yes I would validate the response!
future (2020) has not brought the feature you and I miss
For the reason why this is missing, I also have no answer.
For me, the questions are quite big. :). As my understanding, you would like to ask the validation in REST service that Spring can support.
1. Is it necessary to do such validations also at the RestTemplate level for responses from REST calls?
Actually, it depends on your apps or your business. You can do at Controller or you can do in Service Level or even you can do your custom validation. For me, no one forces you to do anything.
However, as my experience, we should do the validation for sure. So my answer here is YES.
2. Will there be support for that at the RestTemplate to validate responses from rest calls sometime in the future?
I assume that you would like the detail of the validation?! Right?
Spring supports many things to do the validation. For the simple way, you can use PathVariable or RequestParameter.For example:
#GetMapping("/test/{name}")
private String test(#PathVariable(value = "name", required = true) String name){
//...
}
Spring will validate all requests, and respond with 400 Bad Request when the required parameter is missing or has a wrong type...
Spring also supports the JSR 303 Bean Validation: http://beanvalidation.org/1.0/spec/ For example in here:
public class MessageBean {
#NotNull
private String title;
#NotNull
private String message;
// getters/setters/etc
}
Or you would like to do the Custom User Response like:
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleException(MethodArgumentNotValidException exception) {
//....
return ErrorResponse.builder().message(errorMsg).build();
}
Some more details in here: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-validation
So, it depends on the business which requires us to do the validation at any level.
3. If the answer is No: (3) why?
No need to answer this. :)
Hope that helps
Related
I've got a SOAP web-service server using Apache CXF as implementation. Due to some external technical constraint I'd like to be able to rename some XML tags naming an operation parameter (which are deprecated) in the inbound SOAP request. I was reading about using Interceptors for this, but the documentation on how to setup/configure them is not very clear.
My code to publish an endpoint is the following:
Endpoint endpoint = Endpoint.create(
"http://schemas.xmlsoap.org/soap/", new MyServer());
endpoint.publish("ws/endpoint");
Ideally I'd like to add a filter only to a given endpoint (I have several of them).
Apache's documentations about interceptors are quite clear (IMO), anyway, there is a helloworld project (based on spring boot, cxf and maven) in my github profile which you can take a look for setting up interceptors (in fact it's a baisc autentication interceptor).
For setting up an interceptor (e.g InInterceptor), your class should extend AbstractPhaseInterceptor<Message> and override handleMessage(Message message) method, then in the constructor you should declare the phase in which the interceptor is going to be applied. Finally you have to instantiate it and apply in on an Endpoint.
As you said:
rename some XML tags naming an operation parameter (which are
deprecated) in the inbound SOAP request
I think the name of the operation parameter (in WSDL file) is something different from the argument of your web method. Suppose that there is method in your endpoint named addPerson:
#WebMethod
String addPerson(Person person) {
/*method logic*/
}
and Person class:
class Person {
private String firstName;
private String lastName;
private Date birthDate;
//getters and setters
}
in order to map lastName property to a different name, you have to annotate it with
#XmlElement(name = "sureName")
private String lastName;
after applying this anotation, sureName (in wsdl file) is going to be mapped to lastName.
In addition, there is #WebParam annotation which can be used for changing the name of web method arguments:
#WebMethod
String sayHello( #WebParam(name = "sureName") String lastName);
Hope it helps.
I commence in REST and I have some questions:
What type must the controller return? Typically, I'm asking if my Rest #Controller must return Item object as it is or encapsulate it in ResponseEntity in order to specify http-status-code.
What http status code to use in a GET method on a particular item ("/items/2") if the given item does not exists: HttpMediaStatus.OK(200) and null return or HttpStatus.NO_CONTENT(204) and null return ?
Second part: I saw it was possible to specify #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
Context: using Spring Boot, hibernate, REST webservice.
Thank you.
Many questions in one, I'll provide short answers with a bunch of link to relevant articles and the reference documentation.
What type must the controller return?
Depends on your annotation and the RESTful-ness of your service. There are three annotations you can use for controllers: #Controller, #RestController and #RepositoryRestController.
Controller is the base annotation to mark your class as a controller. The return type of the controller endpoint methods can be many things, I invite you to read this dedicated post to get a grasp of it.
When developing a pure-REST service, you will focus on using RestController and RepositoryRestController.
RestControlleris Controller + ResponseBody. It binds the return value of the endpoint method to the web response body:
#RestController
public ItemController {
#RequestMapping("/items/{id}")
public Item getItem(#PathVariable("id") String id) {
Item item = ...
return item;
}
}
With this, when you hit http:/.../api/items/foo, Spring does its magic, automatically converting the item to a ResponseEntity with a relevant 40X status code and some default HTTP headers.
At some point, you will need more control over the status code and headers, while still benefiting from Spring Data REST's settings. That's when you will use RepositoryRestController with a ResponseEntity<Item> as return type, see the example the Spring Data REST reference.
What http status code to use in a GET method on a particular item if the given item does not exists?
Bluntly said: use HttpStatus.NOT_FOUND. You're looking for a resource that does not exist, there's something wrong.
That being said, it is completely up to you to decide how to handle missing resources in your project. If your workflow justifies it, a missing resource could be something completely acceptable that indeed returns a 20X response, though you may expect users of your API to get confused if you haven't warned them or provided some documentation (we are creatures of habits and conventions). But I'd still start with a 404 status code.
(...) #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
#Consumes and #Produces are respectively matched against content-type and accept headers from the request. It's a mean of restricting the input accepted and the output provided by your endpoint method.
Since we're talking about a REST service, communications between clients of the API and the service are expected to be JSON-formatted. Thanks to Spring HATEOAS, the answer are actually formatted with the application/hal+json content-type.
In that scenario, you can indeed not bother with those two annotations. You will need them if you develop a service that accepts different content-types (application/text, application/json, application/xml...) and provides, for instance, HTML views to users of your website and JSON or XML response to automated clients of your service.
For real life examples:
Facebook provides the Graph API for applications to read to/write from its graph, while users happily (?) surf on web pages
Google does the same with the Google Maps API
I am using the Asp.Net Web Api. I would like to be able to filter out certain fields on the response objects based on the connected clients access rights.
Example:
class Foo
{
[AccessFilter("Uberlord")]
string Wibble { get; set; }
string Wobble { get; set; }
}
When returning data the filed Wibble should only be returned if the current users context can satisfy the value of "Uberlord".
There are three avenues that I am exploring but I have not got a working solution:
A custom WebApi MediaTypeFormatter.
A custom json.net IContractResolver.
Some sort of AOP wrapper for controllers that manipulates the response object
My issue with these are:
The custom formatter does not feel like the right place to do it but might be the only option.
The custom json serializer would not have access to the current context so I would have to work that out.
With the first two options you would require specific implementations for each response format, json, xml, some custom format, etc. This would mean that if another response type is supported then a custom formatter / serializer is required to prevent sensitive data leaking.
The AOP controller wrapper would require a lot of reflection.
An additional bonus would be to strip out values from the fields on an inbound request object using the same mechanism.
Have I missed an obvious hook? Has this been solved by another way?
It was actually a lot simpler than I first thought. What I did not realise is that the DelegatingHandler can be used to manipulate the response as well as the request in the Web Api Pipeline.
Lifecycle of an ASP.NET Web API Message
Delegating Handler
Delegating handlers are an extensibility point in the message pipeline allowing you to massage the Request before passing it on to the rest of the pipeline. The response message on its way back has to pass through the Delegating Handler as well, so any response can also be monitored/filtered/updated at this extensibility point.
Delegating Handlers if required, can bypass the rest of the pipeline too and send back and Http Response themselves.
Example
Here is an example implementation of a DelegatingHandler that can either manipulate the response object or replace it altogether.
public class ResponseDataFilterHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result;
//Manipulate content here
var content = response.Content as ObjectContent;
if (content != null && content.Value != null)
{
((SomeObject)content.Value).SomeProperty = null;
}
//Or replace the content
response.Content = new ObjectContent(typeof(object), new object(), new JsonMediaTypeFormatter());
return response;
});
}
}
Microsoft article on how to implement a delegating handler and add it to the pipeline.HTTP Message Handlers in ASP.NET Web API
I have a similar question in the works over here: ASP.NET WebAPI Conditional Serialization based on User Role
A proposed solution that I came up with is to have my ApiController inherit from a BaseApiController which overrides the Initalize function to set the appropriate formatter based on the user's role. I haven't decided if I will go this way yet, but perhaps it will work for you.
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
// If the user is in a sensitive-data access role
controllerContext.Configuration.Formatters.Add(/*My Formatter*/);
// Otherwise use the default ones added in global app_start that defaults to remove sensitive data
}
I'm having trouble setting something up that I'm pretty sure /should/ be easy, so I thought I'd throw it to the crowd. I can't seem to find what I'm looking for elsewhere on the web or on SE.
I am simplifying my project of course, but basically I have a JAX-WS annontated Jersey resource class that looks something like this:
#Path("myresource")
public class MyResource {
#Autowired
MyComplexObjectDAO daoInstance;
#Path("findObject/{id}")
#GET
public MyComplexObject findObject( #PathParam(value="id") String id ) {
return daoInstance.findObject( id );
}
#Path("saveObject")
#PUT
public MyComplexObject saveObject( MyComplexObject objectToSave ) {
MyComplexObject savedObject = daoInstance.saveObject( objectToSave );
return savedObject;
}
}
So you can see I'm autowiring a DAO object using spring, and then I use the DAO methods in the REST handlers.
The 'findObject' call seems to work fine - so far it works exactly as I expect it to.
The 'saveObject' call is not working the way I want and that's what I need some advice on.
You can see that I'm trying to directly take an instance of my complex object as a parameter to the REST method. Additionally I would like to return an instance of the complex object after it's been saved.
I put together some 'client' code for testing this out.
#Test
public void saveTest() {
WebResource wsClient = createWebServiceClient();
MyComplexObject unsavedInstance = createMyComplexObject();
MyComplexObject savedInstance =
wsClient
.path("saveObject")
.accept(MediaType.APPLICATION_XML)
.put(MyComplexObject.class, unsavedInstance);
assertNotNull(savedIntent);
}
Which is returning the following error:
com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:8081/rest/myresource/save returned a response status of 400 Bad Request
I don't see why this isn't working and I think I've tried just about everything I can think of. Any help or direction would be very much appreciated.
Thanks so much!
I see that you call the accept() method in your test client (which means that a "Accept:" header is added to the request, indicating the server what type of representation you would like). However, you don't call the type() method to add a "Content-type:" header and inform the server that you are sending XML data. See http://jersey.java.net/nonav/documentation/latest/client-api.html#d4e644 for examples.
Side remark: your URLs are not RESTful - you should avoid verbs in your path:
So, instead of:
/api/findObject/{id}
/api/saveObject
You should use:
/api/objects/{id}
/api/objects
Last note: to create an object on calling /api/objects, you should do a POST and not a PUT to adhere to REST best practices and widely adopted patterns.
switching to the 'concrete class' solution I alluded to in my earlier comment is what fixed things up for me.
I have to consume a service provided by one of our partners. I was given little direction, but was told the security was to be PasswordDigest. I looked it up and immediatly saw lots of references to WSE, so off I went. It was very easy to implement and in no time I had a standard WSE user token using PasswordDigest sitting in the SOAP headers of my messages.
When we started testing today I was immediatly told (by the error message) that things weren't right. Turns out, out partner doesn't look in the SOAP header, but rather wants the security info in the http header.
I have seen lots of articles on how to add custom http headers to a proxy class, but my proxy inherits from SoapHttpClientProtocol which doesn't have a headers collection to add to. I was looking at making a raw httpWebRequest, but I have a specific method to access that has some complex parameters to deal with (and besides it feels like going backwords).
What is the best way to add custom http headers to a service proxy class that doesn't have a GetWebRequest method?
For reference:
Proxy class decleration:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.3053")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="MtomServiceSoap11", namespace="http://ws.xxxxxxx.com/")]
public partial class MtomServiceService : System.Web.Services.Protocols.SoapHttpClientProtocol {
Target method I need to call:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: System.Xml.Serialization.XmlElementAttribute("uploadDocumentResponse", Namespace="http://ws.edsmtom.citizensfla.com/")]
public uploadDocumentResponse uploadDocument([System.Xml.Serialization.XmlElementAttribute(Namespace="http://ws.xxxxxxx.com/")] uploadDocumentRequest uploadDocumentRequest) {
object[] results = this.Invoke("uploadDocument", new object[] {
uploadDocumentRequest});
return ((uploadDocumentResponse)(results[0]));
}
}
The actual call to the Service is simple. The objects being pass in are not:
request.criteria = docCriteria;
request.document = document;
var result = service.uploadDocument(request);
Thanks.
It figures that 30 minutes after posting I would stumble across the answer. While the proxy class decelaration does not create a GetWebRequest method, its base class System.Web.Services.Protocols.SoapHttpClientProtocol has it and it can be overridden.
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
var request = base.GetWebRequest(uri);
request.Headers.Add("blah", "blah"); // <----
return request;
}